examples/re-gen-modules.lhs

Regex Module Gen

All of the modules that make up the API are generated from the Text.RE.TDFA.ByteString.Lazy module using this script.

The tool is self-testing: run it with no arguments (or cabal test).

{-# LANGUAGE NoImplicitPrelude          #-}
{-# LANGUAGE TemplateHaskell            #-}
{-# LANGUAGE QuasiQuotes                #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE CPP                        #-}

module Main (main) where

import qualified Data.ByteString.Lazy.Char8               as LBS
import qualified Data.Monoid                              as M
import qualified Data.Text                                as T
import           Prelude.Compat
import           System.Directory
import           System.Environment
import           System.Exit
import           System.IO
import           TestKit
import           Text.RE.TDFA.ByteString.Lazy
import           Text.RE.Tools.Sed


type ModPath = String


main :: IO ()
main = do
  (pn,as) <- (,) <$> getProgName <*> getArgs
  case as of
    []        -> test
    ["test"]  -> test
    ["gen"]   -> gen
    _         -> do
      hPutStrLn stderr $ "usage: " ++ pn ++ " [test|gen]"
      exitWith $ ExitFailure 1


------------------------------------------------------------------------
-- Testing
------------------------------------------------------------------------

test :: IO ()
test = do
  createDirectoryIfMissing False "tmp"
  tdfa_ap_ok <- and <$> mapM (test' source_api_mp) tdfa_api_edits
  pcre_ap_ok <- and <$> mapM (test' source_api_mp) pcre_api_edits
  case tdfa_ap_ok && pcre_ap_ok of
    True  -> return ()
    False -> exitWith $ ExitFailure 1

type SedScript = Edits IO RE LBS.ByteString

test' :: ModPath -> (ModPath,SedScript) -> IO Bool
test' src_mp (mp,scr) = do
    putStrLn mp
    tp   <- is_text_present
    lbs  <- read_file $ mod_filepath tp src_mp
    lbs' <- sortImports <$> sed' scr lbs
    write_file tmp_pth lbs'
    cmp  (T.pack tmp_pth) (T.pack $ mod_filepath tp mp)
  where
    tmp_pth = "tmp/prog.hs"


------------------------------------------------------------------------
-- Generating
------------------------------------------------------------------------

gen :: IO ()
gen = do
  mapM_ (gen' source_api_mp) tdfa_api_edits
  mapM_ (gen' source_api_mp) pcre_api_edits
  mapM_ (gen' source_ed_mp ) tdfa_ed_edits
  mapM_ (gen' source_ed_mp ) pcre_ed_edits

gen' :: FilePath -> (ModPath,SedScript) -> IO ()
gen' src_mp (mp,scr) = do
  putStrLn mp
  tp   <- is_text_present
  lbs  <- read_file $ mod_filepath tp src_mp
  lbs' <- sortImports <$> sed' scr lbs
  write_file (mod_filepath tp mp) lbs'


------------------------------------------------------------------------
-- The API edits
------------------------------------------------------------------------

tdfa_api_edits :: [(ModPath,SedScript)]
tdfa_api_edits =
  [ tdfa_api_edit "TDFA.ByteString"       "B.ByteString"    "import qualified Data.ByteString               as B"
  , tdfa_api_edit "TDFA.Sequence"         "(S.Seq Char)"    "import qualified Data.Sequence                 as S"
  , tdfa_api_edit "TDFA.String"           "String"          ""
  , tdfa_api_edit "TDFA.Text"             "T.Text"          "import qualified Data.Text                     as T"
  , tdfa_api_edit "TDFA.Text.Lazy"        "TL.Text"         "import qualified Data.Text.Lazy                as TL"
  ]

pcre_api_edits :: [(ModPath,SedScript)]
pcre_api_edits =
  [ pcre_api_edit "PCRE.ByteString"       "B.ByteString"    "import qualified Data.ByteString               as B"   False
  , pcre_api_edit "PCRE.ByteString.Lazy"  "LBS.ByteString"  "import qualified Data.ByteString.Lazy          as LBS" False
  , pcre_api_edit "PCRE.Sequence"         "(S.Seq Char)"    "import qualified Data.Sequence                 as S"   False
  , pcre_api_edit "PCRE.String"           "String"          ""                                                      False
  , pcre_api_edit "PCRE.Text"             "T.Text"          "import qualified Data.Text                     as T"   True
  , pcre_api_edit "PCRE.Text.Lazy"        "TL.Text"         "import qualified Data.Text.Lazy                as TL"  True
  ]

tdfa_api_edit :: ModPath
              -> LBS.ByteString
              -> LBS.ByteString
              -> (ModPath,SedScript)
tdfa_api_edit mp bs_lbs import_lbs =
    (,) fmp $ Pipe
        [ Template $ SearchReplace module_re $ LBS.pack mp
        , Template $ SearchReplace import_re   import_lbs
        , Template $ SearchReplace bs_re       bs_lbs
        ]
  where
    fmp = "Text.RE." ++ mp

pcre_api_edit :: ModPath
              -> LBS.ByteString
              -> LBS.ByteString
              -> Bool
              -> (ModPath,SedScript)
pcre_api_edit mp bs_lbs import_lbs is_orp =
    (,) fmp $ Pipe $
        [ Template $ SearchReplace tdfa_re     "PCRE"
        , Template $ SearchReplace module_re $ LBS.pack mp
        , Template $ SearchReplace import_re   import_lbs
        , Template $ SearchReplace bs_re       bs_lbs
        ] ++
        [ Template $ SearchReplace orp_re    $ orphan_import mp
          | is_orp
          ]
  where
    fmp = "Text.RE." ++ mp

source_api_mp :: ModPath
source_api_mp = "Text.RE.TDFA.ByteString.Lazy"

orphan_import :: ModPath -> LBS.ByteString
orphan_import mp = "import           Text.Regex." M.<> LBS.pack mp M.<> "()"

tdfa_re, module_re, import_re, bs_re, orp_re :: RE
tdfa_re   = [re|TDFA|]
module_re = [re|TDFA.ByteString.Lazy|]
import_re = [re|import qualified Data.ByteString.Lazy.Char8 *as LBS|]
bs_re     = [re|LBS.ByteString|]
orp_re    = [re|^-- NB regex-base instance imports.*$|]


------------------------------------------------------------------------
-- The ed quasi quoter edits
------------------------------------------------------------------------

source_ed_mp :: ModPath
source_ed_mp = "Text.RE.ZeInternals.SearchReplace.TDFA.ByteString.Lazy"

tdfa_ed_edits :: [(ModPath,SedScript)]
tdfa_ed_edits =
  [ (,) "Text.RE.ZeInternals.SearchReplace.TDFA.ByteString" $ Pipe
      [ Template [ed|SearchReplace.TDFA.ByteString.Lazy///SearchReplace.TDFA.ByteString|]
      , Template [ed|Data.ByteString.Lazy.Char8    as LBS///Data.ByteString.Char8         as B|]
      , Template [ed|LBS.ByteString///B.ByteString|]
      ]
  , (,) "Text.RE.ZeInternals.SearchReplace.TDFA.Sequence" $ Pipe
      [ Template [ed|SearchReplace.TDFA.ByteString.Lazy///SearchReplace.TDFA.Sequence|]
      , Template [ed|Data.ByteString.Lazy.Char8    as LBS///Data.Sequence                 as S|]
      , Template [ed|LBS.ByteString///(S.Seq Char)|]
      ]
  , (,) "Text.RE.ZeInternals.SearchReplace.TDFA.String" $ Pipe
      [ Template [ed|SearchReplace.TDFA.ByteString.Lazy///SearchReplace.TDFA.String|]
      , Template [ed|import qualified Data.ByteString.Lazy.Char8    as LBS///|]
      , Template [ed|LBS.ByteString///String|]
      ]
  , (,) "Text.RE.ZeInternals.SearchReplace.TDFA.Text" $ Pipe
      [ Template [ed|SearchReplace.TDFA.ByteString.Lazy///SearchReplace.TDFA.Text|]
      , Template [ed|Data.ByteString.Lazy.Char8    as LBS///Data.Text                     as T|]
      , Template [ed|LBS.ByteString///T.Text|]
      ]
  , (,) "Text.RE.ZeInternals.SearchReplace.TDFA.Text.Lazy" $ Pipe
      [ Template [ed|SearchReplace.TDFA.ByteString.Lazy///SearchReplace.TDFA.Text.Lazy|]
      , Template [ed|Data.ByteString.Lazy.Char8    as LBS///Data.Text.Lazy                as TL|]
      , Template [ed|LBS.ByteString///TL.Text|]
      ]
  ]

pcre_ed_edits :: [(ModPath,SedScript)]
pcre_ed_edits =
  [ (,) "Text.RE.ZeInternals.SearchReplace.PCRE.ByteString" $ pipe
      [ Template [ed|SearchReplace.TDFA.ByteString.Lazy///SearchReplace.PCRE.ByteString|]
      , Template [ed|Data.ByteString.Lazy.Char8    as LBS///Data.ByteString.Char8         as B|]
      , Template [ed|LBS.ByteString///B.ByteString|]
      ]
  , (,) "Text.RE.ZeInternals.SearchReplace.PCRE.ByteString.Lazy" $ pipe
      [ Template [ed|SearchReplace.TDFA.ByteString.Lazy///SearchReplace.PCRE.ByteString.Lazy|]
      , Template [ed|Text.RE.ZeInternals.TDFA///Text.RE.ZeInternals.PCRE|]
      ]
  , (,) "Text.RE.ZeInternals.SearchReplace.PCRE.Sequence" $ pipe
      [ Template [ed|SearchReplace.TDFA.ByteString.Lazy///SearchReplace.PCRE.Sequence|]
      , Template [ed|Data.ByteString.Lazy.Char8    as LBS///Data.Sequence                 as S|]
      , Template [ed|LBS.ByteString///(S.Seq Char)|]
      , Template [ed|Text.RE.ZeInternals.TDFA///Text.RE.ZeInternals.PCRE|]
      ]
  , (,) "Text.RE.ZeInternals.SearchReplace.PCRE.String" $ pipe
      [ Template [ed|SearchReplace.TDFA.ByteString.Lazy///SearchReplace.PCRE.String|]
      , Template [ed|import qualified Data.ByteString.Lazy.Char8    as LBS///|]
      , Template [ed|LBS.ByteString///String|]
      , Template [ed|Text.RE.ZeInternals.TDFA///Text.RE.ZeInternals.PCRE|]
      ]
  , (,) "Text.RE.ZeInternals.SearchReplace.PCRE.Text" $ pipe
      [ Template [ed|SearchReplace.TDFA.ByteString.Lazy///SearchReplace.PCRE.Text|]
      , Template [ed|Data.ByteString.Lazy.Char8    as LBS///Data.Text                     as T|]
      , Template [ed|LBS.ByteString///T.Text|]
      ]
  , (,) "Text.RE.ZeInternals.SearchReplace.PCRE.Text.Lazy" $ pipe
      [ Template [ed|SearchReplace.TDFA.ByteString.Lazy///SearchReplace.PCRE.Text.Lazy|]
      , Template [ed|Data.ByteString.Lazy.Char8    as LBS///Data.Text.Lazy                as TL|]
      , Template [ed|LBS.ByteString///TL.Text|]
      ]
  ]
  where
    pipe as = Pipe $ as ++
      [ Template [ed|Text.RE.ZeInternals.TDFA///Text.RE.ZeInternals.PCRE|]
      , Template [ed|Text.RE.ZeInternals.SearchReplace.TDFAEdPrime///Text.RE.ZeInternals.SearchReplace.PCREEdPrime|]
      ]


------------------------------------------------------------------------
-- Helpers
------------------------------------------------------------------------

mod_filepath :: Bool -> ModPath -> FilePath
mod_filepath text_present mp = pfx ++ map tr mp ++ ".hs"
  where
    pfx = case text_present of
      True  -> ""
      False -> "src/"

    tr '.' = '/'
    tr c   = c

is_text_present :: IO Bool
is_text_present = doesDirectoryExist "Text"