1 {-# LANGUAGE FlexibleContexts #-}
2 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE StandaloneDeriving #-}
4 {-# OPTIONS_GHC -fno-warn-orphans #-}
5 module Hcompta.Lib.Regex where
7 import Control.Monad (Monad(..))
8 import qualified Data.Array
10 import qualified Data.Char
12 import Data.Eq (Eq(..))
13 import Data.Foldable (concat)
14 import qualified Data.List
15 import Data.String (String)
16 import Data.Text (Text)
17 import Data.Typeable ()
18 import Text.Read(Read(..), read)
19 import qualified Text.Regex.TDFA as R
20 import Text.Regex.TDFA.Common as R
21 import qualified Text.Regex.TDFA.IntArrTrieSet as R.IntArrTrieSet
22 import qualified Text.Regex.TDFA.Text ()
36 type Replacement = String
40 -- | Parse the given 'String' to a 'Regex'.
41 of_String :: String -> R.Regex
42 of_String = R.makeRegex
44 instance Read R.Regex where
45 readsPrec _ s = [(R.makeRegex s, "")]
46 instance Show R.Regex where
48 -- instance Eq Regex where
50 deriving instance Eq a => Eq (R.IntArrTrieSet.TrieSet a)
51 deriving instance Eq R.CompOption
52 deriving instance Eq R.DFA
53 deriving instance Eq R.DT
54 deriving instance Eq R.ExecOption
55 deriving instance Eq R.GroupInfo
56 deriving instance Eq R.Instructions
57 deriving instance Eq R.Transition
58 instance Eq (R.Position -> R.OrbitTransformer) where
60 deriving instance Eq R.Regex
62 -- | Parse the given 'String' to a 'R.Regex' (monadic version).
63 of_StringM :: Monad m => String -> m R.Regex
64 of_StringM = R.makeRegexM
68 match :: R.Regex -> Text -> Bool
71 -- | Synonym to Text.R.Regex.TDFA.'Text.R.Regex.TDFA.=~'.
72 (=~) :: ( R.RegexMaker R.Regex R.CompOption R.ExecOption source
73 , R.RegexContext R.Regex source1 target )
74 => source1 -> source -> target
77 -- | Synonym to Text.R.Regex.TDFA.'Text.R.Regex.TDFA.=~~'.
78 (=~~) :: ( R.RegexMaker R.Regex R.CompOption R.ExecOption source
79 , R.RegexContext R.Regex source1 target,Monad m )
80 => source1 -> source -> m target
85 replace :: R.Regex -> Replacement -> String -> String
87 Data.List.foldl (replace_match repl) s
88 (reverse $ R.match re s :: [R.MatchText String])
90 replace_match :: Replacement -> String -> R.MatchText String -> String
91 replace_match replacement s match_groups =
92 concat [prev, repl, next]
94 ((_, (ofs, len)):_) = Data.Array.elems match_groups
95 -- NOTE: groups should have 0-based indexes,
96 -- and there should always be at least one,
97 -- since this is a match.
98 (prev, next') = Data.List.splitAt ofs s
99 next = Data.List.drop len next'
100 repl = replace_all (of_String "\\\\[0-9]+") (replace_backref match_groups) replacement
102 replace_backref :: R.MatchText String -> String -> String
103 replace_backref match_groups ('\\':s@(_:_))
104 | Data.List.all Data.Char.isDigit s =
106 n | Data.List.elem n $ Data.Array.indices match_groups -> fst ((Data.Array.!) match_groups n)
107 _ -> error $ concat ["no match group exists for backreference \"\\", s, "\""]
108 replace_backref _ s =
109 error $ concat $ ["replace_backref called on non-numeric-backref \"", s, "\""]
111 replace_all :: R.Regex -> (String -> String) -> String -> String
113 concat (reverse $ remaining:done)
115 (_ind, remaining, done) = Data.List.foldl' go (0, s, []) $
116 (R.getAllMatches $ R.match re s::[(Int, Int)])
117 go (ind, prev_ok_next, repl) (ofs, len) =
118 let (prev, ok_next) = Data.List.splitAt (ofs - ind) prev_ok_next
119 (ok, next) = Data.List.splitAt len ok_next in