packages feed

tlex-th (empty) → 0.1.0.0

raw patch · 12 files changed

+695/−0 lines, 12 filesdep +QuickCheckdep +arraydep +basebuild-type:Customsetup-changed

Dependencies added: QuickCheck, array, base, containers, doctest, ghc-prim, hspec, template-haskell, tlex, tlex-core, tlex-th

Files

+ CHANGELOG.md view
@@ -0,0 +1,3 @@+## 0.1.0.0 -- 2021-01-XX++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,5 @@+Apache-2.0 OR MPL-2.0++---++See https://github.com/mizunashi-mana/tlex/blob/master/LICENSE
+ README.md view
@@ -0,0 +1,1 @@+See https://hackage.haskell.org/package/tlex
+ Setup.hs view
@@ -0,0 +1,6 @@+module Main where++import           Distribution.Extra.Doctest (defaultMainWithDoctests)++main :: IO ()+main = defaultMainWithDoctests "doctest"
+ src/Language/Lexer/Tlex/Data/Addr.hs view
@@ -0,0 +1,23 @@+module Language.Lexer.Tlex.Data.Addr (+    addrCodeUnitsLE,+) where++import           Language.Lexer.Tlex.Prelude++import qualified Data.Bits                   as Bits+++addrCodeUnitsLE :: Bits.Bits a => Integral a => Int -> a -> [Word8]+addrCodeUnitsLE us n+    | n >= 0    = take us+        do map+            do \m -> fromInteger do fromIntegral do mod8bit m+            do iterate (`Bits.shiftR` 8) n+    | n == -1   = replicate us 0xFF+    | otherwise = error "unsupported"+    where+        mod8bit = case Bits.bitSizeMaybe n of+            Nothing -> \x -> x `mod` 256+            Just bs+                | bs <= 8   -> \x -> x+                | otherwise -> \x -> x `mod` 256
+ src/Language/Lexer/Tlex/Data/Bits.hs view
@@ -0,0 +1,17 @@+module Language.Lexer.Tlex.Data.Bits (+    maxBitSize,+) where++import           Language.Lexer.Tlex.Prelude++import qualified Data.Bits                   as Bits+++maxBitSize :: Bits.FiniteBits a => Ord a => Num a => a -> Int+maxBitSize n = go 1 2 where+    go i m+        | n < m                     = i+        | i >= Bits.finiteBitSize n = i+        | otherwise                 = go+            do i + 1+            do m * 2
+ src/Language/Lexer/Tlex/Data/TypeableTH.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE TemplateHaskell #-}++module Language.Lexer.Tlex.Data.TypeableTH (+    liftTypeFromTypeable,+    tyConToType,+) where++import           Prelude++import           Data.Foldable       (foldl')+import qualified Data.Typeable       as Typeable+import qualified Language.Haskell.TH as TH++liftTypeFromTypeable :: Typeable.Typeable a => Typeable.Proxy a -> TH.Q TH.Type+liftTypeFromTypeable p = go do Typeable.typeRep p where+    go r0 =+        let (tyCon, rs) = Typeable.splitTyConApp r0+        in foldl'+            do \tq1 tq2 -> [t|$(tq1) $(tq2)|]+            do tyConToType tyCon+            do [ go r | r <- rs ]++-- |+--+-- TODO: correct reifying+-- NOTICE: introduce @reifyType@ by GHC 8.10+--+tyConToType :: Typeable.TyCon -> TH.Q TH.Type+tyConToType tyCon = do+    mn <- TH.lookupTypeName tyConQualifiedName+    case mn of+        Just n  -> pure do TH.ConT n+        Nothing -> case tyConQualifiedName of+            "GHC.Tuple.()" ->+                pure do TH.TupleT 0+            _  ->+                fail do "Missing type: " ++ tyConQualifiedName+    where+        tyConQualifiedName = Typeable.tyConModule tyCon ++ "." ++ Typeable.tyConName tyCon
+ src/Language/Lexer/Tlex/Output/TH.hs view
@@ -0,0 +1,300 @@+{-# LANGUAGE MagicHash       #-}+{-# LANGUAGE TemplateHaskell #-}++module Language.Lexer.Tlex.Output.TH (+    TlexContext (..),+    TlexResult (..),+    Runner (..),+    runRunner,+    TlexTransStateSize (..),+    tlexLookupTlexTransTable,+    TlexArray,+    tlexArray,+    tlexArrayIndex,+    OutputContext (..),+    outputDfa,+) where++import           Language.Lexer.Tlex.Prelude++import qualified Data.Array                        as Array+import qualified Data.Bits                         as Bits+import qualified Data.IntMap.Strict                as IntMap+import qualified GHC.Prim                          as Prim+import qualified GHC.ST                            as ST+import qualified GHC.Types                         as Types+import qualified Language.Haskell.TH               as TH+import qualified Language.Haskell.TH.Syntax        as TH+import qualified Language.Lexer.Tlex.Data.Addr     as Addr+import qualified Language.Lexer.Tlex.Data.Bits     as Bits+import qualified Language.Lexer.Tlex.Data.EnumMap  as EnumMap+import qualified Language.Lexer.Tlex.Machine.DFA   as DFA+import qualified Language.Lexer.Tlex.Machine.State as MState+import           Language.Lexer.Tlex.Runner+import qualified Language.Lexer.Tlex.Syntax        as Tlex+++data TlexTransStateSize+    = TlexTransStateSize8+    | TlexTransStateSize16+    | TlexTransStateSize32+    deriving (Eq, Show, Enum, TH.Lift)++{-# INLINE tlexLookupTlexTransTable #-}+tlexLookupTlexTransTable :: Int -> TlexTransStateSize -> Prim.Addr#+    -> Int -> Int -> Int+tlexLookupTlexTransTable offset unitSize table# s c =+    let !(Types.I# i#) = s `Bits.shiftL` offset + c+    in ST.runST+        do ST.ST \s0# ->+            let !(# s1#, r# #) = case unitSize of+                    TlexTransStateSize8  -> Prim.readInt8OffAddr#  table# i# s0#+                    TlexTransStateSize16 -> Prim.readInt16OffAddr# table# i# s0#+                    TlexTransStateSize32 -> Prim.readInt32OffAddr# table# i# s0#+            in (# s1#, Types.I# r# #)++type TlexArray = Array.Array Int++{-# INLINE tlexArray #-}+tlexArray :: Int -> [a] -> TlexArray a+tlexArray l xs = Array.listArray (0,l) xs++{-# INLINE tlexArrayIndex #-}+tlexArrayIndex :: TlexArray a -> Int -> a+tlexArrayIndex arr i = arr Array.! i++{-+type TlexStartState = ...+type TlexSemanticAction = ...+type TlexCodeUnit = ...++tlexScan :: TlexContext s TlexCodeUnit m => TlexStartState -> m (TlexResult s TlexSemanticAction)+tlexScan s0 = runRunner runner s0+    where+        runner = Runner+            { tlexInitial = thTlexInitial+            , tlexAccept = thTlexAccept+            , tlexTrans = thTlexTrans+            }++thTlexInitial :: Int -> Int+thTlexInitial = \x -> tlexArrayIndex tlexInitialTable x+    where+        table :: TlexArray Int+        table = tlexArray 10 [10,...]++thTlexTrans :: Int -> Int -> Int+thTlexTrans = \s c -> tlexLookupTlexTransTable+    8+    TlexTransTableStateSize8+    "\x02\x00\x00\x00..."#+    s (c - 0)++thTlexAccept :: Int -> Maybe TlexSemanticAction+thTlexAccept = \x -> if x >= 120+        then Nothing+        else tlexArrayIndex table x+    where+        table :: TlexArray (Maybe TlexSemanticAction)+        table = tlexArray 120 [Nothing,...]+-}+data OutputContext = OutputContext+    { outputCtxStartStateTy     :: TH.Type+    , outputCtxCodeUnitTy       :: TH.Type+    , outputCtxCodeUnitBounds   :: (Int, Int)+    , outputCtxSemanticActionTy :: TH.Type+    }+    deriving (Eq, Show)++outputDfa :: OutputContext -> DFA.DFA (TH.Q TH.Exp) -> TH.Q [TH.Dec]+outputDfa ctx dfa = do+    let startStateTyName = TH.mkName "TlexStartState"+        codeUnitTyName = TH.mkName "TlexCodeUnit"+        semanticActionTyName = TH.mkName "TlexSemanticAction"+        tlexScanFnName = TH.mkName "tlexScan"+        thTlexInitialFnName = TH.mkName "thTlexInitial"+        thTlexTransFnName = TH.mkName "thTlexTrans"+        thTlexAcceptFnName = TH.mkName "thTlexAccept"++    let startStateTy = pure @TH.Q do TH.ConT startStateTyName+        codeUnitTy = pure @TH.Q do TH.ConT codeUnitTyName+        semanticActionTy = pure @TH.Q do TH.ConT semanticActionTyName+        thTlexInitialFn = pure @TH.Q do TH.VarE thTlexInitialFnName+        thTlexTransFn = pure @TH.Q do TH.VarE thTlexTransFnName+        thTlexAcceptFn = pure @TH.Q do TH.VarE thTlexAcceptFnName++    sequence+        [ pure do TH.TySynD startStateTyName [] do outputCtxStartStateTy ctx+        , pure do TH.TySynD codeUnitTyName [] do outputCtxCodeUnitTy ctx+        , pure do TH.TySynD semanticActionTyName [] do outputCtxSemanticActionTy ctx++        , TH.SigD tlexScanFnName <$> [t|+            forall s m. TlexContext s $(codeUnitTy) m+                => $(startStateTy) -> m (TlexResult s $(semanticActionTy))+        |]+        , TH.ValD+            do TH.VarP tlexScanFnName+            <$> do TH.NormalB <$> [e|\s0 -> runRunner runner s0|]+            <*> [d|+                runner = Runner+                    $(thTlexInitialFn)+                    $(thTlexAcceptFn)+                    $(thTlexTransFn)+            |]++        , TH.SigD thTlexInitialFnName <$>+            [t|Int -> Int|]+        , outputTlexInitialFn dfa thTlexInitialFnName++        , TH.SigD thTlexTransFnName <$>+            [t|Int -> Int -> Int|]+        , outputTlexTransFn dfa+            do outputCtxCodeUnitBounds ctx+            thTlexTransFnName++        , TH.SigD thTlexAcceptFnName <$>+            [t|Int -> Maybe $(semanticActionTy)|]+        , outputTlexAcceptFn dfa semanticActionTy thTlexAcceptFnName+        ]++outputTlexInitialFn :: DFA.DFA a -> TH.Name -> TH.Q TH.Dec+outputTlexInitialFn DFA.DFA{ dfaInitials } fnName = do+    tableValName <- TH.newName "table"+    TH.ValD+        do TH.VarP fnName+        <$> do TH.NormalB <$>+                [e|\x -> tlexArrayIndex $(pure do TH.VarE tableValName) x|]+        <*> sequence+                [ TH.SigD tableValName <$>+                    [t|TlexArray Int|]+                , tableDec tableValName+                ]+    where+        tableDec :: TH.Name -> TH.Q TH.Dec+        tableDec valName = TH.ValD+            do TH.VarP valName+            <$> do TH.NormalB <$> do+                    (es, l) <- tableList+                    outputTlexArrayLit l es+            <*> pure []++        tableList :: TH.Q ([TH.Exp], Int)+        tableList =+            let (es, l) = sequentialListFromAscList+                    [e|-1|]+                    [ (fromEnum ss, TH.lift do fromEnum sn)+                    | (ss, sn) <- EnumMap.toAscList dfaInitials+                    ]+            in do+                es' <- sequence es+                pure (es', l)++outputTlexTransFn :: DFA.DFA a -> (Int, Int) -> TH.Name -> TH.Q TH.Dec+outputTlexTransFn DFA.DFA{ dfaTrans } (minUnitB, maxUnitB) fnName =+    let ubs = Bits.maxBitSize do maxUnitB - minUnitB+        um =+            do 1 `Bits.shiftL` ubs+            - 1+        l = concatMap+            do \dstState ->+                let smDef = case DFA.dstOtherTrans dstState of+                        Nothing -> -1+                        Just sm -> fromEnum sm+                    dstTrans = DFA.dstTrans dstState+                in map+                    do \i -> case IntMap.lookup i dstTrans of+                        Just sm -> fromEnum sm+                        Nothing -> smDef+                    [0..um]+            do toList dfaTrans+        sbs = Bits.maxBitSize do length dfaTrans - 1+        sbsEnum = if+            | ubs + sbs > 29 -> error "exceed over bit size limited"+            | otherwise      -> stateSize sbs+    in TH.ValD+        do TH.VarP fnName+        <$> do TH.NormalB <$>+                [e|\s c -> tlexLookupTlexTransTable+                    $(unitBitSizeExp ubs)+                    $(TH.lift sbsEnum)+                    $(tableAddrExp sbsEnum l)+                    s (c - $(TH.lift minUnitB))+                |]+        <*> pure []+    where+        unitBitSizeExp ubs = pure+            do TH.LitE do TH.IntegerL do fromIntegral ubs++        stateSize sbs+            | sbs <= 8  = TlexTransStateSize8+            | sbs <= 16 = TlexTransStateSize16+            | otherwise = TlexTransStateSize32++        tableAddrExp ss l =+            let us = case ss of+                    TlexTransStateSize8  -> 1+                    TlexTransStateSize16 -> 2+                    TlexTransStateSize32 -> 4+            in pure+                do TH.LitE+                    do TH.StringPrimL+                        do concatMap+                            do \sn -> Addr.addrCodeUnitsLE us+                                do fromEnum sn+                            do l++outputTlexAcceptFn+    :: DFA.DFA (TH.Q TH.Exp) -> (TH.Q TH.Type) -> TH.Name -> TH.Q TH.Dec+outputTlexAcceptFn DFA.DFA{ dfaTrans } semanticActionTy fnName = do+    tableValName <- TH.newName "table"+    (es, l) <- tableList+    TH.ValD+        do TH.VarP fnName+        <$> do TH.NormalB <$>+                [e|+                    \x -> if x >= $(TH.lift l)+                        then Nothing+                        else tlexArrayIndex $(pure do TH.VarE tableValName) x+                |]+        <*> sequence+                [ TH.SigD tableValName <$>+                    [t|TlexArray (Maybe $(semanticActionTy))|]+                , tableDec tableValName es l+                ]+    where+        tableDec valName es l = TH.ValD+            do TH.VarP valName+            <$> do TH.NormalB <$> outputTlexArrayLit l es+            <*> pure []++        tableList :: TH.Q ([TH.Exp], Int)+        tableList =+            let (es, l) = sequentialListFromAscList+                    [e|Nothing|]+                    do+                        (sn, dstSt) <- MState.arrayAssocs dfaTrans+                        let accExp = case DFA.dstAccepts dstSt of+                                []    -> [e|Nothing|]+                                acc:_ -> [e|Just $(Tlex.accSemanticAction acc)|]+                        pure (fromEnum sn, accExp)+            in do+                es' <- sequence es+                pure (es', l)++outputTlexArrayLit :: Int -> [TH.Exp] -> TH.Q TH.Exp+outputTlexArrayLit l es =+    [e|tlexArray $(TH.lift l) $(pure do TH.ListE es)|]++sequentialListFromAscList :: a -> [(Int, a)] -> ([a], Int)+sequentialListFromAscList v xs =+    let (l0, m) = foldl'+            do \(l, !pi) (i, x) -> (fillV i pi l . (x:), succ i)+            do (id, 0)+            do xs+    in (l0 [], m)+    where+        fillV i !pi l+            | pi == i   = l+            | otherwise = fillV i+                do succ pi+                do l . (v:)
+ src/Language/Lexer/Tlex/Plugin/TH.hs view
@@ -0,0 +1,118 @@+{-# LANGUAGE CPP #-}++module Language.Lexer.Tlex.Plugin.TH (+    TlexTH.TlexContext (..),+    TlexTH.TlexResult (..),+    TlexTH.Runner (..),+    TlexTH.runRunner,+    THScanner (..),+    THScannerBuilderContext,+    THScannerBuilder,+    buildTHScanner,+    buildTHScannerWithReify,+    liftTlexScannerBuilder,+    thLexRule,+    outputScanner,+) where++import           Language.Lexer.Tlex.Prelude++import qualified Language.Haskell.TH                      as TH+import qualified Language.Lexer.Tlex.Data.TypeableTH      as TypeableTH+import qualified Language.Lexer.Tlex.Machine.NFA          as NFA+import qualified Language.Lexer.Tlex.Output.TH            as TlexTH+import qualified Language.Lexer.Tlex.Pipeline.MinDfa      as TlexPipeline+import qualified Language.Lexer.Tlex.Pipeline.Nfa2Dfa     as TlexPipeline+import qualified Language.Lexer.Tlex.Pipeline.Scanner2Nfa as TlexPipeline+import qualified Language.Lexer.Tlex.Syntax               as Tlex++#ifdef DEBUG+import qualified Debug.Trace                              as Debug+#endif+++data THScanner e = THScanner+    { thScannerOutputCtx   :: TlexTH.OutputContext+    , thScannerTlexScanner :: Tlex.Scanner e (TH.Q TH.Exp)+    }++data THScannerBuilderContext s e a = THScannerBuilderContext+    { thScannerBuilderCtxOutputCtx :: TlexTH.OutputContext+    , thScannerBuilderCtxTlexScannerBuilderCtx :: Tlex.ScannerBuilderContext s e (TH.Q TH.Exp)+    }++type THScannerBuilder s e a = State (THScannerBuilderContext s e a)++buildTHScanner :: forall e s a. Enum e => Bounded e+    => TH.Type -> TH.Type -> TH.Type -> THScannerBuilder s e a ()+    -> THScanner e+buildTHScanner codeUnitTy startStateTy actionTy builder =+    let outputCtx = TlexTH.OutputContext+            { outputCtxStartStateTy = startStateTy+            , outputCtxCodeUnitTy = codeUnitTy+            , outputCtxCodeUnitBounds = (+                fromEnum do minBound @e,+                fromEnum do maxBound @e+            )+            , outputCtxSemanticActionTy = actionTy+            }+        tlexScanner = Tlex.buildScanner do+            modify' \ctx0 -> thScannerBuilderCtxTlexScannerBuilderCtx+                do execState builder do+                    THScannerBuilderContext+                        { thScannerBuilderCtxOutputCtx = outputCtx+                        , thScannerBuilderCtxTlexScannerBuilderCtx = ctx0+                        }+    in THScanner+        { thScannerOutputCtx = outputCtx+        , thScannerTlexScanner = tlexScanner+        }++buildTHScannerWithReify :: forall s a e.+    Enum e => Bounded e => Typeable e => Typeable s => Typeable a+    => THScannerBuilder s e a () -> TH.Q (THScanner e)+buildTHScannerWithReify builder = do+    startStateTy <- TypeableTH.liftTypeFromTypeable do Proxy @s+    codeUnitTy <- TypeableTH.liftTypeFromTypeable do Proxy @e+    actionTy <- TypeableTH.liftTypeFromTypeable do Proxy @a+    pure do buildTHScanner codeUnitTy startStateTy actionTy builder++liftTlexScannerBuilder :: Enum e => Tlex.ScannerBuilder s e (TH.Q TH.Exp) a -> THScannerBuilder s e f a+liftTlexScannerBuilder builder = do+    ctx0 <- get+    let (x, tlexCtx1) = runState+            do builder+            do thScannerBuilderCtxTlexScannerBuilderCtx ctx0+    put do ctx0+            { thScannerBuilderCtxTlexScannerBuilderCtx = tlexCtx1+            }+    pure x++thLexRule :: Enum e => Enum s => [s] -> Tlex.Pattern e -> TH.Q (TH.TExp a) -> THScannerBuilder s e a ()+thLexRule ss p act = liftTlexScannerBuilder do Tlex.lexRule ss p do TH.unType <$> act+++outputScanner :: Enum e => THScanner e -> TH.Q [TH.Dec]+outputScanner scanner =+    let outputCtx = thScannerOutputCtx scanner+        nfa =+#ifdef DEBUG+            Debug.trace "building NFA..." do+#endif+            NFA.buildNFA do+                TlexPipeline.scanner2Nfa do thScannerTlexScanner scanner+        dfa =+#ifdef DEBUG+            Debug.trace "building DFA..." do+#endif+            TlexPipeline.nfa2Dfa nfa+        minDfa =+#ifdef DEBUG+            Debug.trace "minizing DFA..." do+#endif+            TlexPipeline.minDfa dfa+    in+#ifdef DEBUG+        Debug.trace "outputing DFA..." do+#endif+        TlexTH.outputDfa outputCtx minDfa
+ test/doctest/Doctest.hs view
@@ -0,0 +1,21 @@+module Main where++import           Prelude++import qualified Build_doctests     as BuildF+import           Control.Monad+import qualified System.Environment as IO+import qualified System.IO          as IO+import           Test.DocTest       (doctest)++main :: IO ()+main = forM_ BuildF.components \(BuildF.Component name flags pkgs sources) -> do+  putStrLn "============================================="+  print name+  putStrLn "---------------------------------------------"+  IO.hFlush IO.stdout+  let args = flags ++ pkgs ++ sources+  IO.unsetEnv "GHC_ENVIRONMENT"+  doctest args+  putStrLn "============================================="+  IO.hFlush IO.stdout
+ test/spec/HSpecDriver.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ tlex-th.cabal view
@@ -0,0 +1,161 @@+cabal-version:       3.0+build-type:          Custom++name:                tlex-th+version:             0.1.0.0+license:             Apache-2.0 OR MPL-2.0+license-file:        LICENSE+copyright:           (c) 2021 Mizunashi Mana+author:              Mizunashi Mana+maintainer:          mizunashi-mana@noreply.git++category:            Parsing+homepage:            https://github.com/mizunashi-mana/tlex+bug-reports:         https://github.com/mizunashi-mana/tlex/issues+synopsis:            TemplateHaskell plugin for Tlex+description:+    Tlex is haskell libraries and toolchains for generating lexical analyzer.+    See also: https://github.com/mizunashi-mana/tlex++extra-source-files:+    README.md+    CHANGELOG.md++source-repository head+  type:     git+  location: https://github.com/mizunashi-mana/tlex.git++flag develop+    default:     False+    manual:      True+    description: Turn on some options for development++common general+    default-language:+        Haskell2010+    default-extensions:+        NoImplicitPrelude+        BangPatterns+        BinaryLiterals+        BlockArguments+        ConstraintKinds+        DataKinds+        DefaultSignatures+        DeriveFoldable+        DeriveFunctor+        DeriveGeneric+        DeriveLift+        DeriveTraversable+        DerivingVia+        DuplicateRecordFields+        EmptyCase+        FlexibleContexts+        FlexibleInstances+        FunctionalDependencies+        GADTs+        InstanceSigs+        LambdaCase+        MagicHash+        MultiParamTypeClasses+        MultiWayIf+        NamedFieldPuns+        NegativeLiterals+        NumericUnderscores+        OverloadedLabels+        PackageImports+        PatternSynonyms+        PolyKinds+        RankNTypes+        ScopedTypeVariables+        StandaloneDeriving+        Strict+        TypeApplications+        TypeFamilies+        TypeOperators+        UnboxedSums+        UnboxedTuples++    if flag(develop)+        ghc-options:+            -Wall+            -Wcompat+            -Wincomplete-uni-patterns+            -Wmonomorphism-restriction+            -Wpartial-fields++            -fprint-explicit-foralls+            -frefinement-level-hole-fits=1++            -dcore-lint++    build-depends:+        base                 >= 4.12.0 && < 4.15,++        -- project depends+        tlex-core            >= 0.1.0 && < 0.2,+        tlex                 >= 0.1.0 && < 0.2,+        ghc-prim             >= 0.5.3 && < 0.7,+        template-haskell     >= 2.14.0 && < 2.17.0,+        array                >= 0.5.3 && < 0.6,+        containers           >= 0.6.0 && < 0.7,++    autogen-modules:+        Paths_tlex_th+    other-modules:+        Paths_tlex_th++custom-setup+    setup-depends:+        base,+        Cabal,+        cabal-doctest,++library+    import:+        general,+    hs-source-dirs:+        src+    exposed-modules:+        Language.Lexer.Tlex.Plugin.TH+        Language.Lexer.Tlex.Data.TypeableTH++        -- internals+        Language.Lexer.Tlex.Output.TH+        Language.Lexer.Tlex.Data.Addr+        Language.Lexer.Tlex.Data.Bits++test-suite doctest+    import:+        general,+    type:+        exitcode-stdio-1.0+    hs-source-dirs:+        test/doctest+    main-is:+        Doctest.hs+    build-depends:+        doctest,+        QuickCheck,+    autogen-modules:+        Build_doctests+    other-modules:+        Build_doctests++test-suite spec+    import:+        general,+    type:+        exitcode-stdio-1.0+    hs-source-dirs:+        test/spec+    main-is:+        HSpecDriver.hs+    ghc-options:+        -Wno-missing-home-modules+    build-tool-depends:+        hspec-discover:hspec-discover,+    build-depends:+        tlex-th,++        hspec,+        QuickCheck,