{-# LANGUAGE CPP, OverloadedStrings, PatternGuards #-}
{-# OPTIONS_HADDOCK hide #-}
module Data.GraphViz.Internal.Util where
import Data.Char (isAsciiLower, isAsciiUpper, isDigit, ord)
import Control.Monad (liftM2)
import Data.Function (on)
import Data.List (groupBy, sortBy)
import Data.Maybe (isJust)
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Text.Lazy (Text)
import qualified Data.Text.Lazy as T
import qualified Data.Text.Lazy.Read as T
#if MIN_VERSION_base(4,8,0)
import Data.Version (Version, makeVersion)
#else
import Data.Version (Version(..))
#endif
isIDString :: Text -> Bool
isIDString :: Text -> Bool
isIDString = Bool -> ((Char, Text) -> Bool) -> Maybe (Char, Text) -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (\(Char
f,Text
os) -> Char -> Bool
frstIDString Char
f Bool -> Bool -> Bool
&& (Char -> Bool) -> Text -> Bool
T.all Char -> Bool
restIDString Text
os)
(Maybe (Char, Text) -> Bool)
-> (Text -> Maybe (Char, Text)) -> Text -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Maybe (Char, Text)
T.uncons
frstIDString :: Char -> Bool
frstIDString :: Char -> Bool
frstIDString Char
c = ((Char -> Bool) -> Bool) -> [Char -> Bool] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any ((Char -> Bool) -> Char -> Bool
forall a b. (a -> b) -> a -> b
$Char
c) [ Char -> Bool
isAsciiUpper
, Char -> Bool
isAsciiLower
, Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
(==) Char
'_'
, (\ Char
x -> Char -> Int
ord Char
x Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
128)
]
restIDString :: Char -> Bool
restIDString :: Char -> Bool
restIDString Char
c = Char -> Bool
frstIDString Char
c Bool -> Bool -> Bool
|| Char -> Bool
isDigit Char
c
isNumString :: Bool -> Text -> Bool
isNumString :: Bool -> Text -> Bool
isNumString Bool
_ Text
"" = Bool
False
isNumString Bool
_ Text
"-" = Bool
False
isNumString Bool
allowE Text
str = case Text -> Maybe (Char, Text)
T.uncons (Text -> Maybe (Char, Text)) -> Text -> Maybe (Char, Text)
forall a b. (a -> b) -> a -> b
$ Text -> Text
T.toLower Text
str of
Just (Char
'-',Text
str') -> Text -> Bool
go Text
str'
Maybe (Char, Text)
_ -> Text -> Bool
go Text
str
where
go :: Text -> Bool
go Text
s = (Text -> Text -> Bool) -> (Text, Text) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Text -> Text -> Bool
go' ((Text, Text) -> Bool) -> (Text, Text) -> Bool
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> Text -> (Text, Text)
T.span Char -> Bool
isDigit Text
s
go' :: Text -> Text -> Bool
go' Text
ds Text
nds
| Text -> Bool
T.null Text
nds = Bool
True
| Text -> Bool
T.null Text
ds Bool -> Bool -> Bool
&& Text
nds Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"." = Bool
False
| Text -> Bool
T.null Text
ds
, Just (Char
'.',Text
nds') <- Text -> Maybe (Char, Text)
T.uncons Text
nds
, Just (Char
d,Text
nds'') <- Text -> Maybe (Char, Text)
T.uncons Text
nds' = Char -> Bool
isDigit Char
d Bool -> Bool -> Bool
&& Text -> Bool
checkEs' Text
nds''
| Just (Char
'.',Text
nds') <- Text -> Maybe (Char, Text)
T.uncons Text
nds = Text -> Bool
checkEs (Text -> Bool) -> Text -> Bool
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> Text -> Text
T.dropWhile Char -> Bool
isDigit Text
nds'
| Text -> Bool
T.null Text
ds = Bool
False
| Bool
otherwise = Text -> Bool
checkEs Text
nds
checkEs' :: Text -> Bool
checkEs' Text
s = case (Char -> Bool) -> Text -> (Text, Text)
T.break (Char
'e' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
==) Text
s of
(Text
"", Text
_) -> Bool
False
(Text
ds,Text
es) -> (Char -> Bool) -> Text -> Bool
T.all Char -> Bool
isDigit Text
ds Bool -> Bool -> Bool
&& Text -> Bool
checkEs Text
es
checkEs :: Text -> Bool
checkEs Text
str' = case Text -> Maybe (Char, Text)
T.uncons Text
str' of
Maybe (Char, Text)
Nothing -> Bool
True
Just (Char
'e',Text
ds) -> Bool
allowE Bool -> Bool -> Bool
&& Text -> Bool
isIntString Text
ds
Maybe (Char, Text)
_ -> Bool
False
toDouble :: Text -> Double
toDouble :: Text -> Double
toDouble Text
str = case Text -> Maybe (Char, Text)
T.uncons (Text -> Maybe (Char, Text)) -> Text -> Maybe (Char, Text)
forall a b. (a -> b) -> a -> b
$ Text -> Text
T.toLower Text
str of
Just (Char
'-', Text
str') -> Text -> Double
toD (Text -> Double) -> Text -> Double
forall a b. (a -> b) -> a -> b
$ Char
'-' Char -> Text -> Text
`T.cons` Text -> Text
adj Text
str'
Maybe (Char, Text)
_ -> Text -> Double
toD (Text -> Double) -> Text -> Double
forall a b. (a -> b) -> a -> b
$ Text -> Text
adj Text
str
where
adj :: Text -> Text
adj Text
s = Char -> Text -> Text
T.cons Char
'0'
(Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ case (Char -> Bool) -> Text -> (Text, Text)
T.span (Char
'.' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
==) Text
s of
(Text
ds, Text
".") | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Text -> Bool
T.null Text
ds -> Text
s Text -> Char -> Text
`T.snoc` Char
'0'
(Text
ds, Text
ds') | Just (Char
'.',Text
es) <- Text -> Maybe (Char, Text)
T.uncons Text
ds'
, Just (Char
'e',Text
_) <- Text -> Maybe (Char, Text)
T.uncons Text
es
-> Text
ds Text -> Char -> Text
`T.snoc` Char
'.' Text -> Char -> Text
`T.snoc` Char
'0' Text -> Text -> Text
`T.append` Text
es
(Text, Text)
_ -> Text
s
toD :: Text -> Double
toD = String -> Double
forall a. Read a => String -> a
read (String -> Double) -> (Text -> String) -> Text -> Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack
isIntString :: Text -> Bool
isIntString :: Text -> Bool
isIntString = Maybe Int -> Bool
forall a. Maybe a -> Bool
isJust (Maybe Int -> Bool) -> (Text -> Maybe Int) -> Text -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Maybe Int
stringToInt
stringToInt :: Text -> Maybe Int
stringToInt :: Text -> Maybe Int
stringToInt Text
str = case Reader Int -> Reader Int
forall a. Num a => Reader a -> Reader a
T.signed Reader Int
forall a. Integral a => Reader a
T.decimal Text
str of
Right (Int
n, Text
"") -> Int -> Maybe Int
forall a. a -> Maybe a
Just Int
n
Either String (Int, Text)
_ -> Maybe Int
forall a. Maybe a
Nothing
escapeQuotes :: String -> String
escapeQuotes :: String -> String
escapeQuotes [] = []
escapeQuotes (Char
'"':String
str) = Char
'\\'Char -> String -> String
forall a. a -> [a] -> [a]
:Char
'"'Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
escapeQuotes String
str
escapeQuotes (Char
c:String
str) = Char
c Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
escapeQuotes String
str
descapeQuotes :: String -> String
descapeQuotes :: String -> String
descapeQuotes [] = []
descapeQuotes (Char
'\\':Char
'"':String
str) = Char
'"' Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
descapeQuotes String
str
descapeQuotes (Char
c:String
str) = Char
c Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
descapeQuotes String
str
isKeyword :: Text -> Bool
isKeyword :: Text -> Bool
isKeyword = (Text -> Set Text -> Bool
forall a. Ord a => a -> Set a -> Bool
`Set.member` Set Text
keywords) (Text -> Bool) -> (Text -> Text) -> Text -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
T.toLower
keywords :: Set Text
keywords :: Set Text
keywords = [Text] -> Set Text
forall a. Ord a => [a] -> Set a
Set.fromList [ Text
"node"
, Text
"edge"
, Text
"graph"
, Text
"digraph"
, Text
"subgraph"
, Text
"strict"
]
createVersion :: [Int] -> Version
#if MIN_VERSION_base(4,8,0)
createVersion :: [Int] -> Version
createVersion = [Int] -> Version
makeVersion
#else
createVersion bs = Version { versionBranch = bs, versionTags = []}
#endif
uniq :: (Ord a) => [a] -> [a]
uniq :: forall a. Ord a => [a] -> [a]
uniq = (a -> a) -> [a] -> [a]
forall b a. Ord b => (a -> b) -> [a] -> [a]
uniqBy a -> a
forall a. a -> a
id
uniqBy :: (Ord b) => (a -> b) -> [a] -> [a]
uniqBy :: forall b a. Ord b => (a -> b) -> [a] -> [a]
uniqBy a -> b
f = ([a] -> a) -> [[a]] -> [a]
forall a b. (a -> b) -> [a] -> [b]
map [a] -> a
forall a. HasCallStack => [a] -> a
head ([[a]] -> [a]) -> ([a] -> [[a]]) -> [a] -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> b) -> [a] -> [[a]]
forall b a. Ord b => (a -> b) -> [a] -> [[a]]
groupSortBy a -> b
f
groupSortBy :: (Ord b) => (a -> b) -> [a] -> [[a]]
groupSortBy :: forall b a. Ord b => (a -> b) -> [a] -> [[a]]
groupSortBy a -> b
f = (a -> a -> Bool) -> [a] -> [[a]]
forall a. (a -> a -> Bool) -> [a] -> [[a]]
groupBy (b -> b -> Bool
forall a. Eq a => a -> a -> Bool
(==) (b -> b -> Bool) -> (a -> b) -> a -> a -> Bool
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` a -> b
f) ([a] -> [[a]]) -> ([a] -> [a]) -> [a] -> [[a]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> a -> Ordering) -> [a] -> [a]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (b -> b -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (b -> b -> Ordering) -> (a -> b) -> a -> a -> Ordering
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` a -> b
f)
groupSortCollectBy :: (Ord b) => (a -> b) -> (a -> c) -> [a] -> [(b,[c])]
groupSortCollectBy :: forall b a c. Ord b => (a -> b) -> (a -> c) -> [a] -> [(b, [c])]
groupSortCollectBy a -> b
f a -> c
g = ([a] -> (b, [c])) -> [[a]] -> [(b, [c])]
forall a b. (a -> b) -> [a] -> [b]
map ((b -> [c] -> (b, [c]))
-> ([a] -> b) -> ([a] -> [c]) -> [a] -> (b, [c])
forall (m :: * -> *) a1 a2 r.
Monad m =>
(a1 -> a2 -> r) -> m a1 -> m a2 -> m r
liftM2 (,) (a -> b
f (a -> b) -> ([a] -> a) -> [a] -> b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> a
forall a. HasCallStack => [a] -> a
head) ((a -> c) -> [a] -> [c]
forall a b. (a -> b) -> [a] -> [b]
map a -> c
g)) ([[a]] -> [(b, [c])]) -> ([a] -> [[a]]) -> [a] -> [(b, [c])]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> b) -> [a] -> [[a]]
forall b a. Ord b => (a -> b) -> [a] -> [[a]]
groupSortBy a -> b
f
bool :: a -> a -> Bool -> a
bool :: forall a. a -> a -> Bool -> a
bool a
f a
t Bool
b = if Bool
b
then a
t
else a
f
isSingle :: [a] -> Bool
isSingle :: forall a. [a] -> Bool
isSingle [a
_] = Bool
True
isSingle [a]
_ = Bool
False