tagsoup 0.14.1 → 0.14.2
raw patch · 36 files changed
+4410/−4392 lines, 36 filesdep +tagsoup
Dependencies added: tagsoup
Files
- CHANGES.txt +2/−0
- Main.hs +0/−59
- TagSoup/Benchmark.hs +0/−201
- TagSoup/Sample.hs +0/−154
- TagSoup/Test.hs +0/−245
- Text/HTML/TagSoup.hs +0/−117
- Text/HTML/TagSoup/Entity.hs +0/−2320
- Text/HTML/TagSoup/Generated.hs +0/−2
- Text/HTML/TagSoup/Implementation.hs +0/−193
- Text/HTML/TagSoup/Manual.hs +0/−13
- Text/HTML/TagSoup/Match.hs +0/−198
- Text/HTML/TagSoup/Options.hs +0/−80
- Text/HTML/TagSoup/Parser.hs +0/−25
- Text/HTML/TagSoup/Render.hs +0/−80
- Text/HTML/TagSoup/Specification.hs +0/−332
- Text/HTML/TagSoup/Tree.hs +0/−118
- Text/HTML/TagSoup/Type.hs +0/−142
- Text/StringLike.hs +0/−96
- src/Text/HTML/TagSoup.hs +117/−0
- src/Text/HTML/TagSoup/Entity.hs +2320/−0
- src/Text/HTML/TagSoup/Generated.hs +2/−0
- src/Text/HTML/TagSoup/Implementation.hs +193/−0
- src/Text/HTML/TagSoup/Manual.hs +13/−0
- src/Text/HTML/TagSoup/Match.hs +198/−0
- src/Text/HTML/TagSoup/Options.hs +80/−0
- src/Text/HTML/TagSoup/Parser.hs +25/−0
- src/Text/HTML/TagSoup/Render.hs +80/−0
- src/Text/HTML/TagSoup/Specification.hs +332/−0
- src/Text/HTML/TagSoup/Tree.hs +118/−0
- src/Text/HTML/TagSoup/Type.hs +142/−0
- src/Text/StringLike.hs +96/−0
- tagsoup.cabal +12/−17
- test/Main.hs +63/−0
- test/TagSoup/Benchmark.hs +202/−0
- test/TagSoup/Sample.hs +158/−0
- test/TagSoup/Test.hs +257/−0
CHANGES.txt view
@@ -1,5 +1,7 @@ Changelog for TagSoup +0.14.2+ #66, make sure positions are correct for lone & characters 0.14.1 #63, add maybeAttrib 0.14
− Main.hs
@@ -1,59 +0,0 @@--module Main(main) where--import System.Environment-import TagSoup.Sample-import TagSoup.Test-import TagSoup.Benchmark-import Data.Char(toLower)---helpMsg :: IO ()-helpMsg = putStr $ unlines $- ["TagSoup, (C) Neil Mitchell 2006-2009"- ,""- ," tagsoup arguments"- ,""- ,"<url> may either be a local file, or a http:// page"- ,""- ] ++ map f res- where- width = maximum $ map (length . fst) res- res = map g actions-- g (nam,msg,Left _) = (nam,msg)- g (nam,msg,Right _) = (nam ++ " <url>",msg)-- f (lhs,rhs) = " " ++ lhs ++ replicate (4 + width - length lhs) ' ' ++ rhs- --actions :: [(String, String, Either (IO ()) (String -> IO ()))]-actions = [("test","Run the test suite",Left test)- ,("grab","Grab a web page",Right grab)- ,("parse","Parse a web page",Right parse)- ,("bench","Benchmark the parsing",Left time)- ,("benchfile","Benchmark the parsing of a file",Right timefile)- ,("validate","Validate a page",Right validate)- ,("lastmodifieddate","Get the wiki.haskell.org last modified date",Left haskellLastModifiedDateTime)- ,("spj","Simon Peyton Jones' papers",Left spjPapers)- ,("ndm","Neil Mitchell's papers",Left ndmPapers)- ,("time","Current time",Left currentTime)- ,("google","Google Tech News",Left googleTechNews)- ,("sequence","Creators on sequence.complete.org",Left rssCreators)- ,("help","This help message",Left helpMsg)- ]--main :: IO ()-main = do- args <- getArgs- case (args, lookup (map toLower $ head args) $ map (\(a,_,c) -> (a,c)) actions) of- ([],_) -> helpMsg- (x:_,Nothing) -> putStrLn ("Error: unknown command " ++ x) >> helpMsg- ([_],Just (Left a)) -> a- (x:xs,Just (Left a)) -> do- putStrLn $ "Warning: expected no arguments to " ++ x ++ " but got: " ++ unwords xs- a- ([_,y],Just (Right a)) -> a y- (x:xs,Just (Right _)) -> do- putStrLn $ "Error: expected exactly one argument to " ++ x ++ " but got: " ++ unwords xs- helpMsg
− TagSoup/Benchmark.hs
@@ -1,201 +0,0 @@-{-# LANGUAGE CPP #-}--module TagSoup.Benchmark where--import Text.HTML.TagSoup--import Control.DeepSeq-import Control.Monad-import Data.List-import Data.Maybe-import System.IO.Unsafe(unsafeInterleaveIO)-import qualified Data.ByteString.Char8 as BS-import qualified Data.ByteString.Lazy.Char8 as LBS-import Data.Time.Clock.POSIX(getPOSIXTime)--conf = 0.95---timefile :: FilePath -> IO ()-timefile file = do- -- use LBS to be most representative of real life- lbs <- LBS.readFile file- let str = LBS.unpack lbs- bs = BS.concat $ LBS.toChunks lbs- () <- LBS.length lbs `seq` length str `seq` BS.length bs `seq` return ()- benchWith (const str, const bs, const lbs) $ benchStatic (toInteger $ LBS.length lbs)---sample :: String-sample = "<this is a test with='attributes' and other=\"things"tested\" /><neil> is </here>" ++- "<!-- comment --> and some just random & test ><<foo></bar><bar><bob href=no>"--nsample = genericLength sample :: Integer--time :: IO ()-time = benchWith (str,bs,lbs) benchVariable- where- str = \i -> concat $ genericReplicate i sample- bs = let s = BS.pack sample in \i -> BS.concat (genericReplicate i s)- lbs = let s = LBS.pack sample in \i -> LBS.concat (genericReplicate i s)----benchWith :: (Integer -> String, Integer -> BS.ByteString, Integer -> LBS.ByteString)- -> ((Integer -> ()) -> IO [String]) -> IO ()-benchWith (str,bs,lbs) bench = do- putStrLn "Timing parseTags in characters/second"- let header = map (:[]) ["(" ++ show (round $ conf * 100) ++ "% confidence)","String","BS","LBS"]- rows <- mapM row $ replicateM 3 [False,True]- mapM_ (putStrLn . strict . grid) $ delay2 $ header : rows- where- row [a,b,c] = do- let header = intercalate "," [g a "pos", g b "warn", g c "merge"]- g b x = (if b then ' ' else '!') : x- f x = bench $ \i -> rnf $ parseTagsOptions parseOptions{optTagPosition=a,optTagWarning=b,optTagTextMerge=c} $ x i- c1 <- f str- c2 <- f bs- c3 <- f lbs- return [[header],c1,c2,c3]-- strict = reverse . reverse--------------------------------------------------------------------------- BENCHMARK ON THE SAMPLE INPUT--disp xs = showUnit (floor xbar) ++ " (~" ++ rng ++ "%)"- where xbar = mean xs- rng = if length xs <= 1 then "?" else show (ceiling $ (range conf xs) * 100 / xbar) --cons x = fmap (x:)---aimTime = 0.3 :: Double -- seconds to aim for-minTime = 0.2 :: Double -- below this a test is considered invalid----- given a number of times to repeat sample, return a list of what--- to display-benchVariable :: (Integer -> ()) -> IO [String]-benchVariable op = cons "?" $ f 10 []- where- f i seen | length seen > 9 = cons (" " ++ disp seen) $ return []- | otherwise = unsafeInterleaveIO $ do- now <- timer $ op i- let cps = if now == 0 then 0 else fromInteger (i * nsample) / now- if now < minTime || (null seen && now < aimTime) then do- let factor = min 7 $ max 2 $ floor $ aimTime / now- cons ("? " ++ disp [cps]) $ f (i * factor) []- else- cons (show (9 - length seen) ++ " " ++ disp (cps:seen)) $ f i (cps:seen)----benchStatic :: Integer -> (Integer -> ()) -> IO [String]-benchStatic nsample op = cons "?" $ f []- where- f seen | length seen > 9 = cons (" " ++ disp seen) $ return []- | otherwise = unsafeInterleaveIO $ do- now <- timer $ op $ genericLength seen- let cps = if now == 0 then 0 else fromInteger nsample / now- cons (show (9 - length seen) ++ " " ++ disp (cps:seen)) $ f (cps:seen)--------------------------------------------------------------------------- UTILITY FUNCTIONS---- | Given a number, show it using a unit and decimal place-showUnit :: Integer -> String-showUnit x = num ++ unit- where- units = " KMGTPEZY"- (use,skip) = splitAt 3 $ show x-- unit = [units !! ((length skip + 2) `div` 3)]-- dot = ((length skip - 1) `mod` 3) + 1- num = a ++ ['.' | b /= ""] ++ b- where (a,b) = splitAt dot use----- copied from the criterion package-getTime :: IO Double-getTime = (fromRational . toRational) `fmap` getPOSIXTime--timer :: () -> IO Double-timer x = do- start <- getTime- () <- return x- end <- getTime- return $ end - start----- display a grid-grid :: [[String]] -> String-grid xs = unlines $ map (concat . zipWith f cols) xs- where cols = map (maximum . map length) $ transpose xs- f n x = x ++ replicate (n+1 - length x) ' '----- display a series of grids over time--- when a grid gets to [] keep its value at that--- when all grids get to [] return []-delay2 :: [[[String]]] -> [[[String]]]-delay2 xs = map (map head) xs : (if all (null . tail) (concat xs) then [] else delay2 $ map (map tl) xs)- where tl (x:xs) = if null xs then x:xs else xs--------------------------------------------------------------------------- INSTANCES--instance NFData a => NFData (Tag a) where- rnf (TagOpen x y) = rnf x `seq` rnf y- rnf (TagClose x) = rnf x- rnf (TagText x) = rnf x- rnf (TagComment x) = rnf x- rnf (TagWarning x) = rnf x- rnf (TagPosition x y) = () -- both are already ! bound---#ifndef BYTESTRING_HAS_NFDATA-# ifdef MIN_VERSION_bytestring-# define BYTESTRING_HAS_NFDATA (MIN_VERSION_bytestring(0,10,0))-# else-# define BYTESTRING_HAS_NFDATA (__GLASGOW_HASKELL__ >= 706)-# endif-#endif--#if !BYTESTRING_HAS_NFDATA-instance NFData LBS.ByteString where- rnf x = LBS.length x `seq` ()--instance NFData BS.ByteString where- rnf x = BS.length x `seq` ()-#endif--------------------------------------------------------------------------- STATISTICS--- Provided by Emily Mitchell--confNs = let (*) = (,) in- [0.95 * 1.96- ,0.90 * 1.644]--size :: [Double] -> Double-size = genericLength--mean :: [Double] -> Double-mean xs = sum xs / size xs--stddev :: [Double] -> Double-stddev xs = sqrt $ sum [sqr (x - xbar) | x <- xs] / size xs- where xbar = mean xs- sqr x = x * x---- given a sample, and a required confidence--- of the mean (i.e. 2.5% = 0.025)-range ::Double -> [Double] -> Double-range conf xs = conf2 * stddev xs / sqrt (size xs)- where conf2 = fromMaybe (error $ "Unknown confidence interval: " ++ show conf) $ lookup conf confNs
− TagSoup/Sample.hs
@@ -1,154 +0,0 @@--module TagSoup.Sample where--import Text.HTML.TagSoup--import Control.Exception-import Control.Monad-import Data.List-import System.Cmd-import System.Directory-import System.Exit-import System.IO---openItem :: String -> IO String-openItem url | not $ "http://" `isPrefixOf` url = readFile url-openItem url = bracket- (openTempFile "." "tagsoup.tmp")- (\(file,hndl) -> removeFile file)- $ \(file,hndl) -> do- hClose hndl- putStrLn $ "Downloading: " ++ url- res <- system $ "wget " ++ url ++ " -O " ++ file- when (res /= ExitSuccess) $ error $ "Failed to download using wget: " ++ url- src <- readFile file- length src `seq` return src---grab :: String -> IO ()-grab x = openItem x >>= putStr--parse :: String -> IO ()-parse x = openItem x >>= putStr . show2 . parseTags- where- show2 [] = "[]"- show2 xs = "[" ++ concat (intersperseNotBroken "\n," $ map show xs) ++ "\n]\n"----- the standard intersperse has a strictness bug which sucks!-intersperseNotBroken :: a -> [a] -> [a]-intersperseNotBroken _ [] = []-intersperseNotBroken sep (x:xs) = x : is xs- where- is [] = []- is (y:ys) = sep : y : is ys---{--<li id="lastmod"> This page was last modified on 9 September 2013, at 22:38.</li>--}-haskellLastModifiedDateTime :: IO ()-haskellLastModifiedDateTime = do- src <- openItem "http://wiki.haskell.org/Haskell"- let lastModifiedDateTime = fromFooter $ parseTags src- putStrLn $ "wiki.haskell.org was last modified on " ++ lastModifiedDateTime- where fromFooter = unwords . drop 6 . words . innerText . take 2 . dropWhile (~/= "<li id=lastmod>")---googleTechNews :: IO ()-googleTechNews = do- tags <- fmap parseTags $ openItem "http://news.google.com/?ned=us&topic=t"- let links = [ ascii name ++ " <" ++ maybe "unknown" shortUrl (lookup "href" atts) ++ ">"- | TagOpen "h2" [("class","title")]:TagText spaces:TagOpen "a" atts:TagText name:_ <- tails tags]- putStr $ unlines links- where- shortUrl x | "http://" `isPrefixOf` x = shortUrl $ drop 7 x- | "www." `isPrefixOf` x = shortUrl $ drop 4 x- | otherwise = takeWhile (/= '/') x-- ascii ('\226':'\128':'\147':xs) = '-' : ascii xs- ascii ('\194':'\163':xs) = "#GBP " ++ ascii xs- ascii (x:xs) = x : ascii xs- ascii [] = []---spjPapers :: IO ()-spjPapers = do- tags <- parseTags <$> openItem "http://research.microsoft.com/en-us/people/simonpj/"- let links = map f $ sections (~== "<A>") $- takeWhile (~/= "<a name=haskell>") $- drop 5 $ dropWhile (~/= "<a name=current>") tags- putStr $ unlines links- where- f :: [Tag String] -> String- f = dequote . unwords . words . fromTagText . head . filter isTagText-- dequote ('\"':xs) | last xs == '\"' = init xs- dequote x = x---ndmPapers :: IO ()-ndmPapers = do- tags <- parseTags <$> openItem "http://community.haskell.org/~ndm/downloads/"- let papers = map f $ sections (~== "<li class=paper>") tags- putStr $ unlines papers- where- f :: [Tag String] -> String- f xs = fromTagText (xs !! 2)---currentTime :: IO ()-currentTime = do- tags <- parseTags <$> openItem "http://www.timeanddate.com/worldclock/uk/london"- let time = fromTagText (dropWhile (~/= "<span id=ct>") tags !! 1)- putStrLn time----type Section = String-data Package = Package {name :: String, desc :: String, href :: String}- deriving Show--hackage :: IO [(Section,[Package])]-hackage = do- tags <- fmap parseTags $ openItem "http://hackage.haskell.org/packages/archive/pkg-list.html"- return $ map parseSect $ partitions (~== "<h3>") tags- where- parseSect xs = (nam, packs)- where- nam = fromTagText $ xs !! 2- packs = map parsePackage $ partitions (~== "<li>") xs-- parsePackage xs =- Package- (fromTagText $ xs !! 2)- (drop 2 $ dropWhile (/= ':') $ fromTagText $ xs !! 4)- (fromAttrib "href" $ xs !! 1)---- rssCreators Example: prints names of story contributors on--- sequence.complete.org. This content is RSS (not HTML), and the selected--- tag uses a different XML namespace "dc:creator".-rssCreators :: IO ()-rssCreators = do- tags <- fmap parseTags $ openItem "http://sequence.complete.org/node/feed"- putStrLn $ unlines $ map names $ partitions (~== "<dc:creator>") tags- where names xs = fromTagText $ xs !! 1---validate :: String -> IO ()-validate x = putStr . unlines . g . f . parseTagsOptions opts =<< openItem x- where- opts = parseOptions{optTagPosition=True, optTagWarning=True}-- f :: [Tag String] -> [String]- f (TagPosition row col:TagWarning warn:rest) =- ("Warning (" ++ show row ++ "," ++ show col ++ "): " ++ warn) : f rest- f (TagWarning warn:rest) =- ("Warning (?,?): " ++ warn) : f rest- f (_:rest) = f rest- f [] = []-- g xs = xs ++ [if n == 0 then "Success, no warnings"- else "Failed, " ++ show n ++ " warning" ++ ['s'|n>1]]- where n = length xs
− TagSoup/Test.hs
@@ -1,245 +0,0 @@-{-# OPTIONS_GHC -fno-warn-deprecations #-}--module TagSoup.Test(test) where--import Text.HTML.TagSoup-import Text.HTML.TagSoup.Entity-import Text.HTML.TagSoup.Match--import Control.Monad-import Data.List-import Test.QuickCheck(Arbitrary(..), Testable(..), quickCheckWithResult, stdArgs,- Args(..), listOf, elements, Result(..))---- * The Test Monad--type Test a = IO a--pass :: Test ()-pass = return ()--runTest :: Test () -> IO ()-runTest x = x >> putStrLn "All tests passed"--(===) :: (Show a, Eq a) => a -> a -> IO ()-a === b = if a == b then pass else fail $ "Does not equal: " ++ show a ++ " =/= " ++ show b--check :: Testable prop => prop -> IO ()-check prop = do- res <- quickCheckWithResult stdArgs{maxSuccess=1000} prop- case res of- Success{} -> pass- _ -> fail "Property failed"--newtype HTML = HTML String deriving Show-instance Arbitrary HTML where- arbitrary = fmap (HTML . concat) $ listOf $ elements frags- where frags = map (:[]) " \n!-</>#&;xy01[]?'\"" ++ ["CDATA","amp","gt","lt"]- shrink (HTML x) = map HTML $ zipWith (++) (inits x) (tail $ tails x)----- * The Main section--test :: IO ()-test = runTest $ do- warnTests- parseTests- optionsTests- renderTests- combiTests- entityTests- lazyTags == lazyTags `seq` pass- matchCombinators---{- |-This routine tests the laziness of the TagSoup parser.-For each critical part of the parser we provide a test input-with a token of infinite size.-Then the output must be infinite too.-If the laziness is broken, then the output will stop early.-We collect the thousandth character of the output of each test case.-If computation of the list stops somewhere,-you have found a laziness stopper.--}---lazyTags :: [Char]-lazyTags = map ((!!1000) . show . parseTags)- [cycle "Rhabarber"- ,repeat '&'- ,"<"++cycle "html"- ,"<html "++cycle "na!me=value "- ,"<html name="++cycle "value"- ,"<html name=\""++cycle "value"- ,"<html name="++cycle "val!ue"- ,"<html "++cycle "name"- ,"</"++cycle "html"- ,"<!-- "++cycle "comment"- ,"<!"++cycle "doctype"- ,"<!DOCTYPE"++cycle " description"- ,cycle "1<2 "- ,"&" ++ cycle "t"- ,"<html name="++cycle "val&ue"- ,"<html name="++cycle "va&l!ue"- ,cycle "& test"-- -- i don't see how this can work unless the junk gets into the AST?- -- ,("</html "++cycle "junk") :- ]----matchCombinators :: Test ()-matchCombinators = do- tagText (const True) (TagText "test") === True- tagText ("test"==) (TagText "test") === True- tagText ("soup"/=) (TagText "test") === True- tagOpenNameLit "table" (TagOpen "table" [("id", "name")]) === True- tagOpenLit "table" (anyAttrLit ("id", "name")) (TagOpen "table" [("id", "name")]) === True- tagOpenLit "table" (anyAttrNameLit "id") (TagOpen "table" [("id", "name")]) === True- tagOpenLit "table" (anyAttrLit ("id", "name")) (TagOpen "table" [("id", "other name")]) === False---parseTests :: Test ()-parseTests = do- parseTags "<!DOCTYPE TEST>" === [TagOpen "!DOCTYPE" [("TEST","")]]- parseTags "<test \"foo bar\">" === [TagOpen "test" [("\"foo",""),("bar\"","")]]- parseTags "<test baz \"foo\">" === [TagOpen "test" [("baz",""),("\"foo\"","")]]- parseTags "<test 'foo bar'>" === [TagOpen "test" [("'foo",""),("bar'","")]]- parseTags "<test bar=''' />" === [TagOpen "test" [("bar",""),("'","")], TagClose "test"]- parseTags "<test2 a b>" === [TagOpen "test2" [("a",""),("b","")]]- parseTags "<test2 ''>" === [TagOpen "test2" [("''","")]]- parseTags "</test foo>" === [TagClose "test"]- parseTags "<test/>" === [TagOpen "test" [], TagClose "test"]- parseTags "<test1 a = b>" === [TagOpen "test1" [("a","b")]]- parseTags "hello & world" === [TagText "hello & world"]- parseTags "hello @ world" === [TagText "hello @ world"]- parseTags "hello @ world" === [TagText "hello @ world"]- parseTags "hello @ world" === [TagText "hello @ world"]- parseTags "hello &haskell; world" === [TagText "hello &haskell; world"]- parseTags "hello \n\t world" === [TagText "hello \n\t world"]- parseTags "<a href=http://www.google.com>" === [TagOpen "a" [("href","http://www.google.com")]]- parseTags "<foo bar=\"bar6baz\">" === [TagOpen "foo" [("bar","bar6baz")]]- parseTags "<foo bar=\"bar&baz\">" === [TagOpen "foo" [("bar","bar&baz")]]- parseTags "hey &how are you" === [TagText "hey &how are you"]- parseTags "hey &how; are you" === [TagText "hey &how; are you"]- parseTags "hey & are you" === [TagText "hey & are you"]- parseTags "hey & are you" === [TagText "hey & are you"]-- -- real cases reported by users- parseTags "↖x≧̸" === [TagText ['\x2196','x','\x2267','\x0338']]- parseTags "test � test" === [TagText "test ? test"]-- parseTags "<a href=\"series.php?view=single&ID=72710\">" === [TagOpen "a" [("href","series.php?view=single&ID=72710")]]-- parseTags "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" ===- [TagOpen "!DOCTYPE" [("HTML",""),("PUBLIC",""),("","-//W3C//DTD HTML 4.01//EN"),("","http://www.w3.org/TR/html4/strict.dtd")]]-- parseTags "<script src=\"http://edge.jobthread.com/feeds/jobroll/?s_user_id=100540&subtype=slashdot\">" ===- [TagOpen "script" [("src","http://edge.jobthread.com/feeds/jobroll/?s_user_id=100540&subtype=slashdot")]]-- parseTags "<a title='foo'bar' href=correct>text" === [TagOpen "a" [("title","foo"),("bar'",""),("href", "correct")],TagText "text"]-- parseTags "<test><![CDATA[Anything goes, <em>even hidden markup</em> & entities]]> but this is outside</test>" ===- [TagOpen "test" [],TagText "Anything goes, <em>even hidden markup</em> & entities but this is outside",TagClose "test"]-- parseTags "<a \r\n href=\"url\">" === [TagOpen "a" [("href","url")]]-- parseTags "<a href='random.php'><img src='strips/130307.jpg' alt='nukular bish'' title='' /></a>" === - [TagOpen "a" [("href","random.php")],TagOpen "img" [("src","strips/130307.jpg"),("alt","nukular bish"),("'",""),("title","")],TagClose "img",TagClose "a"]-- parseTags "<p>some text</p\n<img alt='< &yyy; >' src=\"abc.gif\">" ===- [TagOpen "p" [],TagText "some text",TagClose "p"]-- parseTags "<script> if (x<bomb) </script>" === [TagOpen "script" [], TagText " if (x<bomb) ", TagClose "script"]- parseTags "<script> if (x<bomb) " === [TagOpen "script" [], TagText " if (x<bomb) "]- parseTags "<SCRIPT language=foo> if (x<bomb) </SCRIPT>" === [TagOpen "SCRIPT" [("language","foo")], TagText " if (x<bomb) ", TagClose "SCRIPT"]- parseTags "<script /><test>" === [TagOpen "script" [], TagClose "script", TagOpen "test" []]-- -- some escapes require trailing semicolons, see #28 and #27.- parseTags "one ∣ two" === [TagText "one \8739 two"]- parseTags "one &mid two" === [TagText "one &mid two"]- parseTags "one µ two" === [TagText "one \181 two"]- parseTags "one µ two" === [TagText "one \181 two"]--optionsTests :: Test ()-optionsTests = check $ \(HTML x) -> all (f x) $ replicateM 3 [False,True]- where- f str [pos,warn,merge] =- bool "merge" (not merge || adjacentTagText tags) &&- bool "warn" (warn || all (not . isTagWarning) tags) &&- bool "pos" (if pos then alternatePos tags else all (not . isTagPosition) tags)- where tags = parseTagsOptions parseOptions{optTagPosition=pos,optTagWarning=warn,optTagTextMerge=merge} str- bool x b = b || error ("optionsTests failed with " ++ x ++ " on " ++ show (pos,warn,merge,str,tags))-- -- optTagTextMerge implies no adjacent TagText cells- -- and none separated by only warnings or positions- adjacentTagText = g True -- can the next be a tag text- where g i (x:xs) | isTagText x = i && g False xs- | isTagPosition x || isTagWarning x = g i xs- | otherwise = g True xs- g i [] = True-- -- optTagPosition implies every element must be followed- -- by a position node, no two position nodes must be adjacent- -- and all positions must be increasing- alternatePos (TagPosition l1 c1 : x : TagPosition l2 c2 : xs)- | (l1,c1) <= (l2,c2) && not (isTagPosition x) = alternatePos $ TagPosition l2 c2 : xs- alternatePos [TagPosition l1 c1, x] | not $ isTagPosition x = True- alternatePos [] = True- alternatePos _ = False---renderTests :: Test ()-renderTests = do- let rp = renderTags . parseTags- rp "<test>" === "<test>"- rp "<br></br>" === "<br />"- rp "<script></script>" === "<script></script>"- rp "hello & world" === "hello & world"- rp "<a href=test>" === "<a href=\"test\">"- rp "<a href>" === "<a href>"- rp "<a href?>" === "<a href?>"- rp "<?xml foo?>" === "<?xml foo ?>"- rp "<?xml foo?>" === "<?xml foo ?>"- rp "<!-- neil -->" === "<!-- neil -->"- rp "<a test=\"a'b\">" === "<a test=\"a'b\">"- escapeHTML "this is a &\" <test> '" === "this is a &" <test> '"- check $ \(HTML x) -> let y = rp x in rp y == (y :: String)-- -entityTests :: Test ()-entityTests = do- lookupNumericEntity "65" === Just "A"- lookupNumericEntity "x41" === Just "A"- lookupNumericEntity "x4E" === Just "N"- lookupNumericEntity "x4e" === Just "N"- lookupNumericEntity "X4e" === Just "N"- lookupNumericEntity "Haskell" === Nothing- lookupNumericEntity "" === Nothing- lookupNumericEntity "89439085908539082" === Nothing- lookupNamedEntity "amp" === Just "&"- lookupNamedEntity "haskell" === Nothing- escapeXML "hello world" === "hello world"- escapeXML "hello & world" === "hello & world"---combiTests :: Test ()-combiTests = do- (TagText "test" ~== TagText "" ) === True- (TagText "test" ~== TagText "test") === True- (TagText "test" ~== TagText "soup") === False- (TagText "test" ~== "test") === True- (TagOpen "test" [] ~== "<test>") === True- (TagOpen "test" [] ~== "<soup>") === False- (TagOpen "test" [] ~/= "<soup>") === True- (TagComment "foo" ~== "<!--foo-->") === True- (TagComment "bar" ~== "<!--bar-->") === True---warnTests :: Test ()-warnTests = do- let p = parseTagsOptions parseOptions{optTagPosition=True,optTagWarning=True}- wt x = [(msg,c) | TagWarning msg:TagPosition _ c:_ <- tails $ p x]- wt "neil &foo bar" === [("Unknown entity: foo",10)]
− Text/HTML/TagSoup.hs
@@ -1,117 +0,0 @@-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, PatternGuards #-}--{-|- This module is for working with HTML/XML. It deals with both well-formed XML and- malformed HTML from the web. It features:-- * A lazy parser, based on the HTML 5 specification - see 'parseTags'.-- * A renderer that can write out HTML/XML - see 'renderTags'.-- * Utilities for extracting information from a document - see '~==', 'sections' and 'partitions'.-- The standard practice is to parse a 'String' to @[@'Tag' 'String'@]@ using 'parseTags',- then operate upon it to extract the necessary information.--}--module Text.HTML.TagSoup(- -- * Data structures and parsing- Tag(..), Row, Column, Attribute,- module Text.HTML.TagSoup.Parser,- module Text.HTML.TagSoup.Render,- canonicalizeTags,-- -- Note: the "#tag-identification#" creates an anchor that's linked to from Match.hs- -- * #tag-identification# Tag identification- isTagOpen, isTagClose, isTagText, isTagWarning, isTagPosition,- isTagOpenName, isTagCloseName, isTagComment,-- -- * Extraction- fromTagText, fromAttrib,- maybeTagText, maybeTagWarning,- innerText,-- -- * Utility- sections, partitions,- - -- * Combinators- TagRep(..), (~==),(~/=)- ) where--import Text.HTML.TagSoup.Type-import Text.HTML.TagSoup.Parser-import Text.HTML.TagSoup.Render-import Data.Char-import Data.List (groupBy, tails)-import Text.StringLike----- | Turns all tag names and attributes to lower case and--- converts DOCTYPE to upper case.-canonicalizeTags :: StringLike str => [Tag str] -> [Tag str]-canonicalizeTags = map f- where- f (TagOpen tag attrs) | Just ('!',name) <- uncons tag = TagOpen ('!' `cons` ucase name) attrs- f (TagOpen name attrs) = TagOpen (lcase name) [(lcase k, v) | (k,v) <- attrs]- f (TagClose name) = TagClose (lcase name)- f a = a-- ucase = fromString . map toUpper . toString- lcase = fromString . map toLower . toString----- | Define a class to allow String's or Tag str's to be used as matches-class TagRep a where- -- | Convert a value into a 'Tag'.- toTagRep :: StringLike str => a -> Tag str--instance StringLike str => TagRep (Tag str) where toTagRep = fmap castString--instance TagRep String where- toTagRep x = case parseTags x of- [a] -> toTagRep a- _ -> error $ "When using a TagRep it must be exactly one tag, you gave: " ++ x------ | Performs an inexact match, the first item should be the thing to match.--- If the second item is a blank string, that is considered to match anything.--- For example:------ > (TagText "test" ~== TagText "" ) == True--- > (TagText "test" ~== TagText "test") == True--- > (TagText "test" ~== TagText "soup") == False------ For 'TagOpen' missing attributes on the right are allowed.-(~==) :: (StringLike str, TagRep t) => Tag str -> t -> Bool-(~==) a b = f a (toTagRep b)- where- f (TagText y) (TagText x) = strNull x || x == y- f (TagClose y) (TagClose x) = strNull x || x == y- f (TagOpen y ys) (TagOpen x xs) = (strNull x || x == y) && all g xs- where- g (name,val) | strNull name = val `elem` map snd ys- | strNull val = name `elem` map fst ys- g nameval = nameval `elem` ys- f (TagComment x) (TagComment y) = strNull x || x == y- f (TagWarning x) (TagWarning y) = strNull x || x == y- f (TagPosition x1 x2) (TagPosition y1 y2) = x1 == y1 && x2 == y2- f _ _ = False---- | Negation of '~=='-(~/=) :: (StringLike str, TagRep t) => Tag str -> t -> Bool-(~/=) a b = not (a ~== b)------ | This function takes a list, and returns all suffixes whose--- first item matches the predicate.-sections :: (a -> Bool) -> [a] -> [[a]]-sections p = filter (p . head) . init . tails---- | This function is similar to 'sections', but splits the list--- so no element appears in any two partitions.-partitions :: (a -> Bool) -> [a] -> [[a]]-partitions p =- let notp = not . p- in groupBy (const notp) . dropWhile notp
− Text/HTML/TagSoup/Entity.hs
@@ -1,2320 +0,0 @@--- | This module converts between HTML/XML entities (i.e. @&@) and--- the characters they represent.-module Text.HTML.TagSoup.Entity(- lookupEntity, lookupNamedEntity, lookupNumericEntity,- escapeXML,- xmlEntities, htmlEntities- ) where--import Data.Char (chr, ord)-import qualified Data.IntMap as IntMap-import Data.Ix-import qualified Data.Map as Map-import Numeric (readHex)----- | Lookup an entity, using 'lookupNumericEntity' if it starts with--- @#@ and 'lookupNamedEntity' otherwise-lookupEntity :: String -> Maybe String-lookupEntity ('#':xs) = lookupNumericEntity xs-lookupEntity xs = lookupNamedEntity xs----- | Lookup a numeric entity, the leading @\'#\'@ must have already been removed.------ > lookupNumericEntity "65" == Just "A"--- > lookupNumericEntity "x41" == Just "A"--- > lookupNumericEntity "x4E" === Just "N"--- > lookupNumericEntity "x4e" === Just "N"--- > lookupNumericEntity "X4e" === Just "N"--- > lookupNumericEntity "Haskell" == Nothing--- > lookupNumericEntity "" == Nothing--- > lookupNumericEntity "89439085908539082" == Nothing-lookupNumericEntity :: String -> Maybe String-lookupNumericEntity = f- -- entity = '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'- where- f (x:xs) | x `elem` "xX" = g [('0','9'),('a','f'),('A','F')] readHex xs- f xs = g [('0','9')] reads xs-- g :: [(Char,Char)] -> ReadS Integer -> String -> Maybe String- g valid reader xs = do- let test b = if b then Just () else Nothing- test $ isValid valid xs- test $ not $ null xs- case reader xs of- [(a,"")] -> do- test $ inRange (toInteger $ ord minBound, toInteger $ ord maxBound) a- return [chr $ fromInteger a]- _ -> Nothing-- isValid :: [(Char,Char)] -> String -> Bool- isValid valid xs = all (\x -> any (`inRange` x) valid) xs----- | Lookup a named entity, using 'htmlEntities'------ > lookupNamedEntity "amp" == Just "&"--- > lookupNamedEntity "haskell" == Nothing-lookupNamedEntity :: String -> Maybe String-lookupNamedEntity = \x -> Map.lookup x mp- where mp = Map.fromList htmlEntities----- | Escape an XML string.------ > escapeXML "hello world" == "hello world"--- > escapeXML "hello & world" == "hello & world"-escapeXML :: String -> String-escapeXML = concatMap $ \x -> IntMap.findWithDefault [x] (ord x) mp- where mp = IntMap.fromList [(ord b, "&"++a++";") | (a,[b]) <- xmlEntities]----- | A table mapping XML entity names to resolved strings. All strings are a single character long.--- Does /not/ include @apos@ as Internet Explorer does not know about it.-xmlEntities :: [(String, String)]-xmlEntities = let a*b = (a,[b]) in- ["quot" * '"'- ,"amp" * '&'- -- ,"apos" * '\'' -- Internet Explorer does not know that- ,"lt" * '<'- ,"gt" * '>'- ]---- | A table mapping HTML entity names to resolved strings. Most resolved strings are a single character long,--- but some (e.g. @"ngeqq"@) are two characters long. The list is taken from--- <http://www.w3.org/TR/html5/syntax.html#named-character-references>.-htmlEntities :: [(String, String)]-htmlEntities = let (*) = (,) in- ["Aacute" * "\x00C1"- ,"aacute" * "\x00E1"- ,"Aacute;" * "\x00C1"- ,"aacute;" * "\x00E1"- ,"Abreve;" * "\x0102"- ,"abreve;" * "\x0103"- ,"ac;" * "\x223E"- ,"acd;" * "\x223F"- ,"acE;" * "\x223E\x0333"- ,"Acirc" * "\x00C2"- ,"acirc" * "\x00E2"- ,"Acirc;" * "\x00C2"- ,"acirc;" * "\x00E2"- ,"acute" * "\x00B4"- ,"acute;" * "\x00B4"- ,"Acy;" * "\x0410"- ,"acy;" * "\x0430"- ,"AElig" * "\x00C6"- ,"aelig" * "\x00E6"- ,"AElig;" * "\x00C6"- ,"aelig;" * "\x00E6"- ,"af;" * "\x2061"- ,"Afr;" * "\x1D504"- ,"afr;" * "\x1D51E"- ,"Agrave" * "\x00C0"- ,"agrave" * "\x00E0"- ,"Agrave;" * "\x00C0"- ,"agrave;" * "\x00E0"- ,"alefsym;" * "\x2135"- ,"aleph;" * "\x2135"- ,"Alpha;" * "\x0391"- ,"alpha;" * "\x03B1"- ,"Amacr;" * "\x0100"- ,"amacr;" * "\x0101"- ,"amalg;" * "\x2A3F"- ,"AMP" * "\x0026"- ,"amp" * "\x0026"- ,"AMP;" * "\x0026"- ,"amp;" * "\x0026"- ,"and;" * "\x2227"- ,"And;" * "\x2A53"- ,"andand;" * "\x2A55"- ,"andd;" * "\x2A5C"- ,"andslope;" * "\x2A58"- ,"andv;" * "\x2A5A"- ,"ang;" * "\x2220"- ,"ange;" * "\x29A4"- ,"angle;" * "\x2220"- ,"angmsd;" * "\x2221"- ,"angmsdaa;" * "\x29A8"- ,"angmsdab;" * "\x29A9"- ,"angmsdac;" * "\x29AA"- ,"angmsdad;" * "\x29AB"- ,"angmsdae;" * "\x29AC"- ,"angmsdaf;" * "\x29AD"- ,"angmsdag;" * "\x29AE"- ,"angmsdah;" * "\x29AF"- ,"angrt;" * "\x221F"- ,"angrtvb;" * "\x22BE"- ,"angrtvbd;" * "\x299D"- ,"angsph;" * "\x2222"- ,"angst;" * "\x00C5"- ,"angzarr;" * "\x237C"- ,"Aogon;" * "\x0104"- ,"aogon;" * "\x0105"- ,"Aopf;" * "\x1D538"- ,"aopf;" * "\x1D552"- ,"ap;" * "\x2248"- ,"apacir;" * "\x2A6F"- ,"ape;" * "\x224A"- ,"apE;" * "\x2A70"- ,"apid;" * "\x224B"- ,"apos;" * "\x0027"- ,"ApplyFunction;" * "\x2061"- ,"approx;" * "\x2248"- ,"approxeq;" * "\x224A"- ,"Aring" * "\x00C5"- ,"aring" * "\x00E5"- ,"Aring;" * "\x00C5"- ,"aring;" * "\x00E5"- ,"Ascr;" * "\x1D49C"- ,"ascr;" * "\x1D4B6"- ,"Assign;" * "\x2254"- ,"ast;" * "\x002A"- ,"asymp;" * "\x2248"- ,"asympeq;" * "\x224D"- ,"Atilde" * "\x00C3"- ,"atilde" * "\x00E3"- ,"Atilde;" * "\x00C3"- ,"atilde;" * "\x00E3"- ,"Auml" * "\x00C4"- ,"auml" * "\x00E4"- ,"Auml;" * "\x00C4"- ,"auml;" * "\x00E4"- ,"awconint;" * "\x2233"- ,"awint;" * "\x2A11"- ,"backcong;" * "\x224C"- ,"backepsilon;" * "\x03F6"- ,"backprime;" * "\x2035"- ,"backsim;" * "\x223D"- ,"backsimeq;" * "\x22CD"- ,"Backslash;" * "\x2216"- ,"Barv;" * "\x2AE7"- ,"barvee;" * "\x22BD"- ,"barwed;" * "\x2305"- ,"Barwed;" * "\x2306"- ,"barwedge;" * "\x2305"- ,"bbrk;" * "\x23B5"- ,"bbrktbrk;" * "\x23B6"- ,"bcong;" * "\x224C"- ,"Bcy;" * "\x0411"- ,"bcy;" * "\x0431"- ,"bdquo;" * "\x201E"- ,"becaus;" * "\x2235"- ,"Because;" * "\x2235"- ,"because;" * "\x2235"- ,"bemptyv;" * "\x29B0"- ,"bepsi;" * "\x03F6"- ,"bernou;" * "\x212C"- ,"Bernoullis;" * "\x212C"- ,"Beta;" * "\x0392"- ,"beta;" * "\x03B2"- ,"beth;" * "\x2136"- ,"between;" * "\x226C"- ,"Bfr;" * "\x1D505"- ,"bfr;" * "\x1D51F"- ,"bigcap;" * "\x22C2"- ,"bigcirc;" * "\x25EF"- ,"bigcup;" * "\x22C3"- ,"bigodot;" * "\x2A00"- ,"bigoplus;" * "\x2A01"- ,"bigotimes;" * "\x2A02"- ,"bigsqcup;" * "\x2A06"- ,"bigstar;" * "\x2605"- ,"bigtriangledown;" * "\x25BD"- ,"bigtriangleup;" * "\x25B3"- ,"biguplus;" * "\x2A04"- ,"bigvee;" * "\x22C1"- ,"bigwedge;" * "\x22C0"- ,"bkarow;" * "\x290D"- ,"blacklozenge;" * "\x29EB"- ,"blacksquare;" * "\x25AA"- ,"blacktriangle;" * "\x25B4"- ,"blacktriangledown;" * "\x25BE"- ,"blacktriangleleft;" * "\x25C2"- ,"blacktriangleright;" * "\x25B8"- ,"blank;" * "\x2423"- ,"blk12;" * "\x2592"- ,"blk14;" * "\x2591"- ,"blk34;" * "\x2593"- ,"block;" * "\x2588"- ,"bne;" * "\x003D\x20E5"- ,"bnequiv;" * "\x2261\x20E5"- ,"bnot;" * "\x2310"- ,"bNot;" * "\x2AED"- ,"Bopf;" * "\x1D539"- ,"bopf;" * "\x1D553"- ,"bot;" * "\x22A5"- ,"bottom;" * "\x22A5"- ,"bowtie;" * "\x22C8"- ,"boxbox;" * "\x29C9"- ,"boxdl;" * "\x2510"- ,"boxdL;" * "\x2555"- ,"boxDl;" * "\x2556"- ,"boxDL;" * "\x2557"- ,"boxdr;" * "\x250C"- ,"boxdR;" * "\x2552"- ,"boxDr;" * "\x2553"- ,"boxDR;" * "\x2554"- ,"boxh;" * "\x2500"- ,"boxH;" * "\x2550"- ,"boxhd;" * "\x252C"- ,"boxHd;" * "\x2564"- ,"boxhD;" * "\x2565"- ,"boxHD;" * "\x2566"- ,"boxhu;" * "\x2534"- ,"boxHu;" * "\x2567"- ,"boxhU;" * "\x2568"- ,"boxHU;" * "\x2569"- ,"boxminus;" * "\x229F"- ,"boxplus;" * "\x229E"- ,"boxtimes;" * "\x22A0"- ,"boxul;" * "\x2518"- ,"boxuL;" * "\x255B"- ,"boxUl;" * "\x255C"- ,"boxUL;" * "\x255D"- ,"boxur;" * "\x2514"- ,"boxuR;" * "\x2558"- ,"boxUr;" * "\x2559"- ,"boxUR;" * "\x255A"- ,"boxv;" * "\x2502"- ,"boxV;" * "\x2551"- ,"boxvh;" * "\x253C"- ,"boxvH;" * "\x256A"- ,"boxVh;" * "\x256B"- ,"boxVH;" * "\x256C"- ,"boxvl;" * "\x2524"- ,"boxvL;" * "\x2561"- ,"boxVl;" * "\x2562"- ,"boxVL;" * "\x2563"- ,"boxvr;" * "\x251C"- ,"boxvR;" * "\x255E"- ,"boxVr;" * "\x255F"- ,"boxVR;" * "\x2560"- ,"bprime;" * "\x2035"- ,"Breve;" * "\x02D8"- ,"breve;" * "\x02D8"- ,"brvbar" * "\x00A6"- ,"brvbar;" * "\x00A6"- ,"Bscr;" * "\x212C"- ,"bscr;" * "\x1D4B7"- ,"bsemi;" * "\x204F"- ,"bsim;" * "\x223D"- ,"bsime;" * "\x22CD"- ,"bsol;" * "\x005C"- ,"bsolb;" * "\x29C5"- ,"bsolhsub;" * "\x27C8"- ,"bull;" * "\x2022"- ,"bullet;" * "\x2022"- ,"bump;" * "\x224E"- ,"bumpe;" * "\x224F"- ,"bumpE;" * "\x2AAE"- ,"Bumpeq;" * "\x224E"- ,"bumpeq;" * "\x224F"- ,"Cacute;" * "\x0106"- ,"cacute;" * "\x0107"- ,"cap;" * "\x2229"- ,"Cap;" * "\x22D2"- ,"capand;" * "\x2A44"- ,"capbrcup;" * "\x2A49"- ,"capcap;" * "\x2A4B"- ,"capcup;" * "\x2A47"- ,"capdot;" * "\x2A40"- ,"CapitalDifferentialD;" * "\x2145"- ,"caps;" * "\x2229\xFE00"- ,"caret;" * "\x2041"- ,"caron;" * "\x02C7"- ,"Cayleys;" * "\x212D"- ,"ccaps;" * "\x2A4D"- ,"Ccaron;" * "\x010C"- ,"ccaron;" * "\x010D"- ,"Ccedil" * "\x00C7"- ,"ccedil" * "\x00E7"- ,"Ccedil;" * "\x00C7"- ,"ccedil;" * "\x00E7"- ,"Ccirc;" * "\x0108"- ,"ccirc;" * "\x0109"- ,"Cconint;" * "\x2230"- ,"ccups;" * "\x2A4C"- ,"ccupssm;" * "\x2A50"- ,"Cdot;" * "\x010A"- ,"cdot;" * "\x010B"- ,"cedil" * "\x00B8"- ,"cedil;" * "\x00B8"- ,"Cedilla;" * "\x00B8"- ,"cemptyv;" * "\x29B2"- ,"cent" * "\x00A2"- ,"cent;" * "\x00A2"- ,"CenterDot;" * "\x00B7"- ,"centerdot;" * "\x00B7"- ,"Cfr;" * "\x212D"- ,"cfr;" * "\x1D520"- ,"CHcy;" * "\x0427"- ,"chcy;" * "\x0447"- ,"check;" * "\x2713"- ,"checkmark;" * "\x2713"- ,"Chi;" * "\x03A7"- ,"chi;" * "\x03C7"- ,"cir;" * "\x25CB"- ,"circ;" * "\x02C6"- ,"circeq;" * "\x2257"- ,"circlearrowleft;" * "\x21BA"- ,"circlearrowright;" * "\x21BB"- ,"circledast;" * "\x229B"- ,"circledcirc;" * "\x229A"- ,"circleddash;" * "\x229D"- ,"CircleDot;" * "\x2299"- ,"circledR;" * "\x00AE"- ,"circledS;" * "\x24C8"- ,"CircleMinus;" * "\x2296"- ,"CirclePlus;" * "\x2295"- ,"CircleTimes;" * "\x2297"- ,"cire;" * "\x2257"- ,"cirE;" * "\x29C3"- ,"cirfnint;" * "\x2A10"- ,"cirmid;" * "\x2AEF"- ,"cirscir;" * "\x29C2"- ,"ClockwiseContourIntegral;" * "\x2232"- ,"CloseCurlyDoubleQuote;" * "\x201D"- ,"CloseCurlyQuote;" * "\x2019"- ,"clubs;" * "\x2663"- ,"clubsuit;" * "\x2663"- ,"colon;" * "\x003A"- ,"Colon;" * "\x2237"- ,"colone;" * "\x2254"- ,"Colone;" * "\x2A74"- ,"coloneq;" * "\x2254"- ,"comma;" * "\x002C"- ,"commat;" * "\x0040"- ,"comp;" * "\x2201"- ,"compfn;" * "\x2218"- ,"complement;" * "\x2201"- ,"complexes;" * "\x2102"- ,"cong;" * "\x2245"- ,"congdot;" * "\x2A6D"- ,"Congruent;" * "\x2261"- ,"conint;" * "\x222E"- ,"Conint;" * "\x222F"- ,"ContourIntegral;" * "\x222E"- ,"Copf;" * "\x2102"- ,"copf;" * "\x1D554"- ,"coprod;" * "\x2210"- ,"Coproduct;" * "\x2210"- ,"COPY" * "\x00A9"- ,"copy" * "\x00A9"- ,"COPY;" * "\x00A9"- ,"copy;" * "\x00A9"- ,"copysr;" * "\x2117"- ,"CounterClockwiseContourIntegral;" * "\x2233"- ,"crarr;" * "\x21B5"- ,"cross;" * "\x2717"- ,"Cross;" * "\x2A2F"- ,"Cscr;" * "\x1D49E"- ,"cscr;" * "\x1D4B8"- ,"csub;" * "\x2ACF"- ,"csube;" * "\x2AD1"- ,"csup;" * "\x2AD0"- ,"csupe;" * "\x2AD2"- ,"ctdot;" * "\x22EF"- ,"cudarrl;" * "\x2938"- ,"cudarrr;" * "\x2935"- ,"cuepr;" * "\x22DE"- ,"cuesc;" * "\x22DF"- ,"cularr;" * "\x21B6"- ,"cularrp;" * "\x293D"- ,"cup;" * "\x222A"- ,"Cup;" * "\x22D3"- ,"cupbrcap;" * "\x2A48"- ,"CupCap;" * "\x224D"- ,"cupcap;" * "\x2A46"- ,"cupcup;" * "\x2A4A"- ,"cupdot;" * "\x228D"- ,"cupor;" * "\x2A45"- ,"cups;" * "\x222A\xFE00"- ,"curarr;" * "\x21B7"- ,"curarrm;" * "\x293C"- ,"curlyeqprec;" * "\x22DE"- ,"curlyeqsucc;" * "\x22DF"- ,"curlyvee;" * "\x22CE"- ,"curlywedge;" * "\x22CF"- ,"curren" * "\x00A4"- ,"curren;" * "\x00A4"- ,"curvearrowleft;" * "\x21B6"- ,"curvearrowright;" * "\x21B7"- ,"cuvee;" * "\x22CE"- ,"cuwed;" * "\x22CF"- ,"cwconint;" * "\x2232"- ,"cwint;" * "\x2231"- ,"cylcty;" * "\x232D"- ,"dagger;" * "\x2020"- ,"Dagger;" * "\x2021"- ,"daleth;" * "\x2138"- ,"darr;" * "\x2193"- ,"Darr;" * "\x21A1"- ,"dArr;" * "\x21D3"- ,"dash;" * "\x2010"- ,"dashv;" * "\x22A3"- ,"Dashv;" * "\x2AE4"- ,"dbkarow;" * "\x290F"- ,"dblac;" * "\x02DD"- ,"Dcaron;" * "\x010E"- ,"dcaron;" * "\x010F"- ,"Dcy;" * "\x0414"- ,"dcy;" * "\x0434"- ,"DD;" * "\x2145"- ,"dd;" * "\x2146"- ,"ddagger;" * "\x2021"- ,"ddarr;" * "\x21CA"- ,"DDotrahd;" * "\x2911"- ,"ddotseq;" * "\x2A77"- ,"deg" * "\x00B0"- ,"deg;" * "\x00B0"- ,"Del;" * "\x2207"- ,"Delta;" * "\x0394"- ,"delta;" * "\x03B4"- ,"demptyv;" * "\x29B1"- ,"dfisht;" * "\x297F"- ,"Dfr;" * "\x1D507"- ,"dfr;" * "\x1D521"- ,"dHar;" * "\x2965"- ,"dharl;" * "\x21C3"- ,"dharr;" * "\x21C2"- ,"DiacriticalAcute;" * "\x00B4"- ,"DiacriticalDot;" * "\x02D9"- ,"DiacriticalDoubleAcute;" * "\x02DD"- ,"DiacriticalGrave;" * "\x0060"- ,"DiacriticalTilde;" * "\x02DC"- ,"diam;" * "\x22C4"- ,"Diamond;" * "\x22C4"- ,"diamond;" * "\x22C4"- ,"diamondsuit;" * "\x2666"- ,"diams;" * "\x2666"- ,"die;" * "\x00A8"- ,"DifferentialD;" * "\x2146"- ,"digamma;" * "\x03DD"- ,"disin;" * "\x22F2"- ,"div;" * "\x00F7"- ,"divide" * "\x00F7"- ,"divide;" * "\x00F7"- ,"divideontimes;" * "\x22C7"- ,"divonx;" * "\x22C7"- ,"DJcy;" * "\x0402"- ,"djcy;" * "\x0452"- ,"dlcorn;" * "\x231E"- ,"dlcrop;" * "\x230D"- ,"dollar;" * "\x0024"- ,"Dopf;" * "\x1D53B"- ,"dopf;" * "\x1D555"- ,"Dot;" * "\x00A8"- ,"dot;" * "\x02D9"- ,"DotDot;" * "\x20DC"- ,"doteq;" * "\x2250"- ,"doteqdot;" * "\x2251"- ,"DotEqual;" * "\x2250"- ,"dotminus;" * "\x2238"- ,"dotplus;" * "\x2214"- ,"dotsquare;" * "\x22A1"- ,"doublebarwedge;" * "\x2306"- ,"DoubleContourIntegral;" * "\x222F"- ,"DoubleDot;" * "\x00A8"- ,"DoubleDownArrow;" * "\x21D3"- ,"DoubleLeftArrow;" * "\x21D0"- ,"DoubleLeftRightArrow;" * "\x21D4"- ,"DoubleLeftTee;" * "\x2AE4"- ,"DoubleLongLeftArrow;" * "\x27F8"- ,"DoubleLongLeftRightArrow;" * "\x27FA"- ,"DoubleLongRightArrow;" * "\x27F9"- ,"DoubleRightArrow;" * "\x21D2"- ,"DoubleRightTee;" * "\x22A8"- ,"DoubleUpArrow;" * "\x21D1"- ,"DoubleUpDownArrow;" * "\x21D5"- ,"DoubleVerticalBar;" * "\x2225"- ,"DownArrow;" * "\x2193"- ,"downarrow;" * "\x2193"- ,"Downarrow;" * "\x21D3"- ,"DownArrowBar;" * "\x2913"- ,"DownArrowUpArrow;" * "\x21F5"- ,"DownBreve;" * "\x0311"- ,"downdownarrows;" * "\x21CA"- ,"downharpoonleft;" * "\x21C3"- ,"downharpoonright;" * "\x21C2"- ,"DownLeftRightVector;" * "\x2950"- ,"DownLeftTeeVector;" * "\x295E"- ,"DownLeftVector;" * "\x21BD"- ,"DownLeftVectorBar;" * "\x2956"- ,"DownRightTeeVector;" * "\x295F"- ,"DownRightVector;" * "\x21C1"- ,"DownRightVectorBar;" * "\x2957"- ,"DownTee;" * "\x22A4"- ,"DownTeeArrow;" * "\x21A7"- ,"drbkarow;" * "\x2910"- ,"drcorn;" * "\x231F"- ,"drcrop;" * "\x230C"- ,"Dscr;" * "\x1D49F"- ,"dscr;" * "\x1D4B9"- ,"DScy;" * "\x0405"- ,"dscy;" * "\x0455"- ,"dsol;" * "\x29F6"- ,"Dstrok;" * "\x0110"- ,"dstrok;" * "\x0111"- ,"dtdot;" * "\x22F1"- ,"dtri;" * "\x25BF"- ,"dtrif;" * "\x25BE"- ,"duarr;" * "\x21F5"- ,"duhar;" * "\x296F"- ,"dwangle;" * "\x29A6"- ,"DZcy;" * "\x040F"- ,"dzcy;" * "\x045F"- ,"dzigrarr;" * "\x27FF"- ,"Eacute" * "\x00C9"- ,"eacute" * "\x00E9"- ,"Eacute;" * "\x00C9"- ,"eacute;" * "\x00E9"- ,"easter;" * "\x2A6E"- ,"Ecaron;" * "\x011A"- ,"ecaron;" * "\x011B"- ,"ecir;" * "\x2256"- ,"Ecirc" * "\x00CA"- ,"ecirc" * "\x00EA"- ,"Ecirc;" * "\x00CA"- ,"ecirc;" * "\x00EA"- ,"ecolon;" * "\x2255"- ,"Ecy;" * "\x042D"- ,"ecy;" * "\x044D"- ,"eDDot;" * "\x2A77"- ,"Edot;" * "\x0116"- ,"edot;" * "\x0117"- ,"eDot;" * "\x2251"- ,"ee;" * "\x2147"- ,"efDot;" * "\x2252"- ,"Efr;" * "\x1D508"- ,"efr;" * "\x1D522"- ,"eg;" * "\x2A9A"- ,"Egrave" * "\x00C8"- ,"egrave" * "\x00E8"- ,"Egrave;" * "\x00C8"- ,"egrave;" * "\x00E8"- ,"egs;" * "\x2A96"- ,"egsdot;" * "\x2A98"- ,"el;" * "\x2A99"- ,"Element;" * "\x2208"- ,"elinters;" * "\x23E7"- ,"ell;" * "\x2113"- ,"els;" * "\x2A95"- ,"elsdot;" * "\x2A97"- ,"Emacr;" * "\x0112"- ,"emacr;" * "\x0113"- ,"empty;" * "\x2205"- ,"emptyset;" * "\x2205"- ,"EmptySmallSquare;" * "\x25FB"- ,"emptyv;" * "\x2205"- ,"EmptyVerySmallSquare;" * "\x25AB"- ,"emsp13;" * "\x2004"- ,"emsp14;" * "\x2005"- ,"emsp;" * "\x2003"- ,"ENG;" * "\x014A"- ,"eng;" * "\x014B"- ,"ensp;" * "\x2002"- ,"Eogon;" * "\x0118"- ,"eogon;" * "\x0119"- ,"Eopf;" * "\x1D53C"- ,"eopf;" * "\x1D556"- ,"epar;" * "\x22D5"- ,"eparsl;" * "\x29E3"- ,"eplus;" * "\x2A71"- ,"epsi;" * "\x03B5"- ,"Epsilon;" * "\x0395"- ,"epsilon;" * "\x03B5"- ,"epsiv;" * "\x03F5"- ,"eqcirc;" * "\x2256"- ,"eqcolon;" * "\x2255"- ,"eqsim;" * "\x2242"- ,"eqslantgtr;" * "\x2A96"- ,"eqslantless;" * "\x2A95"- ,"Equal;" * "\x2A75"- ,"equals;" * "\x003D"- ,"EqualTilde;" * "\x2242"- ,"equest;" * "\x225F"- ,"Equilibrium;" * "\x21CC"- ,"equiv;" * "\x2261"- ,"equivDD;" * "\x2A78"- ,"eqvparsl;" * "\x29E5"- ,"erarr;" * "\x2971"- ,"erDot;" * "\x2253"- ,"escr;" * "\x212F"- ,"Escr;" * "\x2130"- ,"esdot;" * "\x2250"- ,"esim;" * "\x2242"- ,"Esim;" * "\x2A73"- ,"Eta;" * "\x0397"- ,"eta;" * "\x03B7"- ,"ETH" * "\x00D0"- ,"eth" * "\x00F0"- ,"ETH;" * "\x00D0"- ,"eth;" * "\x00F0"- ,"Euml" * "\x00CB"- ,"euml" * "\x00EB"- ,"Euml;" * "\x00CB"- ,"euml;" * "\x00EB"- ,"euro;" * "\x20AC"- ,"excl;" * "\x0021"- ,"exist;" * "\x2203"- ,"Exists;" * "\x2203"- ,"expectation;" * "\x2130"- ,"ExponentialE;" * "\x2147"- ,"exponentiale;" * "\x2147"- ,"fallingdotseq;" * "\x2252"- ,"Fcy;" * "\x0424"- ,"fcy;" * "\x0444"- ,"female;" * "\x2640"- ,"ffilig;" * "\xFB03"- ,"fflig;" * "\xFB00"- ,"ffllig;" * "\xFB04"- ,"Ffr;" * "\x1D509"- ,"ffr;" * "\x1D523"- ,"filig;" * "\xFB01"- ,"FilledSmallSquare;" * "\x25FC"- ,"FilledVerySmallSquare;" * "\x25AA"- ,"fjlig;" * "\x0066\x006A"- ,"flat;" * "\x266D"- ,"fllig;" * "\xFB02"- ,"fltns;" * "\x25B1"- ,"fnof;" * "\x0192"- ,"Fopf;" * "\x1D53D"- ,"fopf;" * "\x1D557"- ,"ForAll;" * "\x2200"- ,"forall;" * "\x2200"- ,"fork;" * "\x22D4"- ,"forkv;" * "\x2AD9"- ,"Fouriertrf;" * "\x2131"- ,"fpartint;" * "\x2A0D"- ,"frac12" * "\x00BD"- ,"frac12;" * "\x00BD"- ,"frac13;" * "\x2153"- ,"frac14" * "\x00BC"- ,"frac14;" * "\x00BC"- ,"frac15;" * "\x2155"- ,"frac16;" * "\x2159"- ,"frac18;" * "\x215B"- ,"frac23;" * "\x2154"- ,"frac25;" * "\x2156"- ,"frac34" * "\x00BE"- ,"frac34;" * "\x00BE"- ,"frac35;" * "\x2157"- ,"frac38;" * "\x215C"- ,"frac45;" * "\x2158"- ,"frac56;" * "\x215A"- ,"frac58;" * "\x215D"- ,"frac78;" * "\x215E"- ,"frasl;" * "\x2044"- ,"frown;" * "\x2322"- ,"Fscr;" * "\x2131"- ,"fscr;" * "\x1D4BB"- ,"gacute;" * "\x01F5"- ,"Gamma;" * "\x0393"- ,"gamma;" * "\x03B3"- ,"Gammad;" * "\x03DC"- ,"gammad;" * "\x03DD"- ,"gap;" * "\x2A86"- ,"Gbreve;" * "\x011E"- ,"gbreve;" * "\x011F"- ,"Gcedil;" * "\x0122"- ,"Gcirc;" * "\x011C"- ,"gcirc;" * "\x011D"- ,"Gcy;" * "\x0413"- ,"gcy;" * "\x0433"- ,"Gdot;" * "\x0120"- ,"gdot;" * "\x0121"- ,"ge;" * "\x2265"- ,"gE;" * "\x2267"- ,"gel;" * "\x22DB"- ,"gEl;" * "\x2A8C"- ,"geq;" * "\x2265"- ,"geqq;" * "\x2267"- ,"geqslant;" * "\x2A7E"- ,"ges;" * "\x2A7E"- ,"gescc;" * "\x2AA9"- ,"gesdot;" * "\x2A80"- ,"gesdoto;" * "\x2A82"- ,"gesdotol;" * "\x2A84"- ,"gesl;" * "\x22DB\xFE00"- ,"gesles;" * "\x2A94"- ,"Gfr;" * "\x1D50A"- ,"gfr;" * "\x1D524"- ,"gg;" * "\x226B"- ,"Gg;" * "\x22D9"- ,"ggg;" * "\x22D9"- ,"gimel;" * "\x2137"- ,"GJcy;" * "\x0403"- ,"gjcy;" * "\x0453"- ,"gl;" * "\x2277"- ,"gla;" * "\x2AA5"- ,"glE;" * "\x2A92"- ,"glj;" * "\x2AA4"- ,"gnap;" * "\x2A8A"- ,"gnapprox;" * "\x2A8A"- ,"gnE;" * "\x2269"- ,"gne;" * "\x2A88"- ,"gneq;" * "\x2A88"- ,"gneqq;" * "\x2269"- ,"gnsim;" * "\x22E7"- ,"Gopf;" * "\x1D53E"- ,"gopf;" * "\x1D558"- ,"grave;" * "\x0060"- ,"GreaterEqual;" * "\x2265"- ,"GreaterEqualLess;" * "\x22DB"- ,"GreaterFullEqual;" * "\x2267"- ,"GreaterGreater;" * "\x2AA2"- ,"GreaterLess;" * "\x2277"- ,"GreaterSlantEqual;" * "\x2A7E"- ,"GreaterTilde;" * "\x2273"- ,"gscr;" * "\x210A"- ,"Gscr;" * "\x1D4A2"- ,"gsim;" * "\x2273"- ,"gsime;" * "\x2A8E"- ,"gsiml;" * "\x2A90"- ,"GT" * "\x003E"- ,"gt" * "\x003E"- ,"GT;" * "\x003E"- ,"gt;" * "\x003E"- ,"Gt;" * "\x226B"- ,"gtcc;" * "\x2AA7"- ,"gtcir;" * "\x2A7A"- ,"gtdot;" * "\x22D7"- ,"gtlPar;" * "\x2995"- ,"gtquest;" * "\x2A7C"- ,"gtrapprox;" * "\x2A86"- ,"gtrarr;" * "\x2978"- ,"gtrdot;" * "\x22D7"- ,"gtreqless;" * "\x22DB"- ,"gtreqqless;" * "\x2A8C"- ,"gtrless;" * "\x2277"- ,"gtrsim;" * "\x2273"- ,"gvertneqq;" * "\x2269\xFE00"- ,"gvnE;" * "\x2269\xFE00"- ,"Hacek;" * "\x02C7"- ,"hairsp;" * "\x200A"- ,"half;" * "\x00BD"- ,"hamilt;" * "\x210B"- ,"HARDcy;" * "\x042A"- ,"hardcy;" * "\x044A"- ,"harr;" * "\x2194"- ,"hArr;" * "\x21D4"- ,"harrcir;" * "\x2948"- ,"harrw;" * "\x21AD"- ,"Hat;" * "\x005E"- ,"hbar;" * "\x210F"- ,"Hcirc;" * "\x0124"- ,"hcirc;" * "\x0125"- ,"hearts;" * "\x2665"- ,"heartsuit;" * "\x2665"- ,"hellip;" * "\x2026"- ,"hercon;" * "\x22B9"- ,"Hfr;" * "\x210C"- ,"hfr;" * "\x1D525"- ,"HilbertSpace;" * "\x210B"- ,"hksearow;" * "\x2925"- ,"hkswarow;" * "\x2926"- ,"hoarr;" * "\x21FF"- ,"homtht;" * "\x223B"- ,"hookleftarrow;" * "\x21A9"- ,"hookrightarrow;" * "\x21AA"- ,"Hopf;" * "\x210D"- ,"hopf;" * "\x1D559"- ,"horbar;" * "\x2015"- ,"HorizontalLine;" * "\x2500"- ,"Hscr;" * "\x210B"- ,"hscr;" * "\x1D4BD"- ,"hslash;" * "\x210F"- ,"Hstrok;" * "\x0126"- ,"hstrok;" * "\x0127"- ,"HumpDownHump;" * "\x224E"- ,"HumpEqual;" * "\x224F"- ,"hybull;" * "\x2043"- ,"hyphen;" * "\x2010"- ,"Iacute" * "\x00CD"- ,"iacute" * "\x00ED"- ,"Iacute;" * "\x00CD"- ,"iacute;" * "\x00ED"- ,"ic;" * "\x2063"- ,"Icirc" * "\x00CE"- ,"icirc" * "\x00EE"- ,"Icirc;" * "\x00CE"- ,"icirc;" * "\x00EE"- ,"Icy;" * "\x0418"- ,"icy;" * "\x0438"- ,"Idot;" * "\x0130"- ,"IEcy;" * "\x0415"- ,"iecy;" * "\x0435"- ,"iexcl" * "\x00A1"- ,"iexcl;" * "\x00A1"- ,"iff;" * "\x21D4"- ,"Ifr;" * "\x2111"- ,"ifr;" * "\x1D526"- ,"Igrave" * "\x00CC"- ,"igrave" * "\x00EC"- ,"Igrave;" * "\x00CC"- ,"igrave;" * "\x00EC"- ,"ii;" * "\x2148"- ,"iiiint;" * "\x2A0C"- ,"iiint;" * "\x222D"- ,"iinfin;" * "\x29DC"- ,"iiota;" * "\x2129"- ,"IJlig;" * "\x0132"- ,"ijlig;" * "\x0133"- ,"Im;" * "\x2111"- ,"Imacr;" * "\x012A"- ,"imacr;" * "\x012B"- ,"image;" * "\x2111"- ,"ImaginaryI;" * "\x2148"- ,"imagline;" * "\x2110"- ,"imagpart;" * "\x2111"- ,"imath;" * "\x0131"- ,"imof;" * "\x22B7"- ,"imped;" * "\x01B5"- ,"Implies;" * "\x21D2"- ,"in;" * "\x2208"- ,"incare;" * "\x2105"- ,"infin;" * "\x221E"- ,"infintie;" * "\x29DD"- ,"inodot;" * "\x0131"- ,"int;" * "\x222B"- ,"Int;" * "\x222C"- ,"intcal;" * "\x22BA"- ,"integers;" * "\x2124"- ,"Integral;" * "\x222B"- ,"intercal;" * "\x22BA"- ,"Intersection;" * "\x22C2"- ,"intlarhk;" * "\x2A17"- ,"intprod;" * "\x2A3C"- ,"InvisibleComma;" * "\x2063"- ,"InvisibleTimes;" * "\x2062"- ,"IOcy;" * "\x0401"- ,"iocy;" * "\x0451"- ,"Iogon;" * "\x012E"- ,"iogon;" * "\x012F"- ,"Iopf;" * "\x1D540"- ,"iopf;" * "\x1D55A"- ,"Iota;" * "\x0399"- ,"iota;" * "\x03B9"- ,"iprod;" * "\x2A3C"- ,"iquest" * "\x00BF"- ,"iquest;" * "\x00BF"- ,"Iscr;" * "\x2110"- ,"iscr;" * "\x1D4BE"- ,"isin;" * "\x2208"- ,"isindot;" * "\x22F5"- ,"isinE;" * "\x22F9"- ,"isins;" * "\x22F4"- ,"isinsv;" * "\x22F3"- ,"isinv;" * "\x2208"- ,"it;" * "\x2062"- ,"Itilde;" * "\x0128"- ,"itilde;" * "\x0129"- ,"Iukcy;" * "\x0406"- ,"iukcy;" * "\x0456"- ,"Iuml" * "\x00CF"- ,"iuml" * "\x00EF"- ,"Iuml;" * "\x00CF"- ,"iuml;" * "\x00EF"- ,"Jcirc;" * "\x0134"- ,"jcirc;" * "\x0135"- ,"Jcy;" * "\x0419"- ,"jcy;" * "\x0439"- ,"Jfr;" * "\x1D50D"- ,"jfr;" * "\x1D527"- ,"jmath;" * "\x0237"- ,"Jopf;" * "\x1D541"- ,"jopf;" * "\x1D55B"- ,"Jscr;" * "\x1D4A5"- ,"jscr;" * "\x1D4BF"- ,"Jsercy;" * "\x0408"- ,"jsercy;" * "\x0458"- ,"Jukcy;" * "\x0404"- ,"jukcy;" * "\x0454"- ,"Kappa;" * "\x039A"- ,"kappa;" * "\x03BA"- ,"kappav;" * "\x03F0"- ,"Kcedil;" * "\x0136"- ,"kcedil;" * "\x0137"- ,"Kcy;" * "\x041A"- ,"kcy;" * "\x043A"- ,"Kfr;" * "\x1D50E"- ,"kfr;" * "\x1D528"- ,"kgreen;" * "\x0138"- ,"KHcy;" * "\x0425"- ,"khcy;" * "\x0445"- ,"KJcy;" * "\x040C"- ,"kjcy;" * "\x045C"- ,"Kopf;" * "\x1D542"- ,"kopf;" * "\x1D55C"- ,"Kscr;" * "\x1D4A6"- ,"kscr;" * "\x1D4C0"- ,"lAarr;" * "\x21DA"- ,"Lacute;" * "\x0139"- ,"lacute;" * "\x013A"- ,"laemptyv;" * "\x29B4"- ,"lagran;" * "\x2112"- ,"Lambda;" * "\x039B"- ,"lambda;" * "\x03BB"- ,"lang;" * "\x27E8"- ,"Lang;" * "\x27EA"- ,"langd;" * "\x2991"- ,"langle;" * "\x27E8"- ,"lap;" * "\x2A85"- ,"Laplacetrf;" * "\x2112"- ,"laquo" * "\x00AB"- ,"laquo;" * "\x00AB"- ,"larr;" * "\x2190"- ,"Larr;" * "\x219E"- ,"lArr;" * "\x21D0"- ,"larrb;" * "\x21E4"- ,"larrbfs;" * "\x291F"- ,"larrfs;" * "\x291D"- ,"larrhk;" * "\x21A9"- ,"larrlp;" * "\x21AB"- ,"larrpl;" * "\x2939"- ,"larrsim;" * "\x2973"- ,"larrtl;" * "\x21A2"- ,"lat;" * "\x2AAB"- ,"latail;" * "\x2919"- ,"lAtail;" * "\x291B"- ,"late;" * "\x2AAD"- ,"lates;" * "\x2AAD\xFE00"- ,"lbarr;" * "\x290C"- ,"lBarr;" * "\x290E"- ,"lbbrk;" * "\x2772"- ,"lbrace;" * "\x007B"- ,"lbrack;" * "\x005B"- ,"lbrke;" * "\x298B"- ,"lbrksld;" * "\x298F"- ,"lbrkslu;" * "\x298D"- ,"Lcaron;" * "\x013D"- ,"lcaron;" * "\x013E"- ,"Lcedil;" * "\x013B"- ,"lcedil;" * "\x013C"- ,"lceil;" * "\x2308"- ,"lcub;" * "\x007B"- ,"Lcy;" * "\x041B"- ,"lcy;" * "\x043B"- ,"ldca;" * "\x2936"- ,"ldquo;" * "\x201C"- ,"ldquor;" * "\x201E"- ,"ldrdhar;" * "\x2967"- ,"ldrushar;" * "\x294B"- ,"ldsh;" * "\x21B2"- ,"le;" * "\x2264"- ,"lE;" * "\x2266"- ,"LeftAngleBracket;" * "\x27E8"- ,"LeftArrow;" * "\x2190"- ,"leftarrow;" * "\x2190"- ,"Leftarrow;" * "\x21D0"- ,"LeftArrowBar;" * "\x21E4"- ,"LeftArrowRightArrow;" * "\x21C6"- ,"leftarrowtail;" * "\x21A2"- ,"LeftCeiling;" * "\x2308"- ,"LeftDoubleBracket;" * "\x27E6"- ,"LeftDownTeeVector;" * "\x2961"- ,"LeftDownVector;" * "\x21C3"- ,"LeftDownVectorBar;" * "\x2959"- ,"LeftFloor;" * "\x230A"- ,"leftharpoondown;" * "\x21BD"- ,"leftharpoonup;" * "\x21BC"- ,"leftleftarrows;" * "\x21C7"- ,"LeftRightArrow;" * "\x2194"- ,"leftrightarrow;" * "\x2194"- ,"Leftrightarrow;" * "\x21D4"- ,"leftrightarrows;" * "\x21C6"- ,"leftrightharpoons;" * "\x21CB"- ,"leftrightsquigarrow;" * "\x21AD"- ,"LeftRightVector;" * "\x294E"- ,"LeftTee;" * "\x22A3"- ,"LeftTeeArrow;" * "\x21A4"- ,"LeftTeeVector;" * "\x295A"- ,"leftthreetimes;" * "\x22CB"- ,"LeftTriangle;" * "\x22B2"- ,"LeftTriangleBar;" * "\x29CF"- ,"LeftTriangleEqual;" * "\x22B4"- ,"LeftUpDownVector;" * "\x2951"- ,"LeftUpTeeVector;" * "\x2960"- ,"LeftUpVector;" * "\x21BF"- ,"LeftUpVectorBar;" * "\x2958"- ,"LeftVector;" * "\x21BC"- ,"LeftVectorBar;" * "\x2952"- ,"leg;" * "\x22DA"- ,"lEg;" * "\x2A8B"- ,"leq;" * "\x2264"- ,"leqq;" * "\x2266"- ,"leqslant;" * "\x2A7D"- ,"les;" * "\x2A7D"- ,"lescc;" * "\x2AA8"- ,"lesdot;" * "\x2A7F"- ,"lesdoto;" * "\x2A81"- ,"lesdotor;" * "\x2A83"- ,"lesg;" * "\x22DA\xFE00"- ,"lesges;" * "\x2A93"- ,"lessapprox;" * "\x2A85"- ,"lessdot;" * "\x22D6"- ,"lesseqgtr;" * "\x22DA"- ,"lesseqqgtr;" * "\x2A8B"- ,"LessEqualGreater;" * "\x22DA"- ,"LessFullEqual;" * "\x2266"- ,"LessGreater;" * "\x2276"- ,"lessgtr;" * "\x2276"- ,"LessLess;" * "\x2AA1"- ,"lesssim;" * "\x2272"- ,"LessSlantEqual;" * "\x2A7D"- ,"LessTilde;" * "\x2272"- ,"lfisht;" * "\x297C"- ,"lfloor;" * "\x230A"- ,"Lfr;" * "\x1D50F"- ,"lfr;" * "\x1D529"- ,"lg;" * "\x2276"- ,"lgE;" * "\x2A91"- ,"lHar;" * "\x2962"- ,"lhard;" * "\x21BD"- ,"lharu;" * "\x21BC"- ,"lharul;" * "\x296A"- ,"lhblk;" * "\x2584"- ,"LJcy;" * "\x0409"- ,"ljcy;" * "\x0459"- ,"ll;" * "\x226A"- ,"Ll;" * "\x22D8"- ,"llarr;" * "\x21C7"- ,"llcorner;" * "\x231E"- ,"Lleftarrow;" * "\x21DA"- ,"llhard;" * "\x296B"- ,"lltri;" * "\x25FA"- ,"Lmidot;" * "\x013F"- ,"lmidot;" * "\x0140"- ,"lmoust;" * "\x23B0"- ,"lmoustache;" * "\x23B0"- ,"lnap;" * "\x2A89"- ,"lnapprox;" * "\x2A89"- ,"lnE;" * "\x2268"- ,"lne;" * "\x2A87"- ,"lneq;" * "\x2A87"- ,"lneqq;" * "\x2268"- ,"lnsim;" * "\x22E6"- ,"loang;" * "\x27EC"- ,"loarr;" * "\x21FD"- ,"lobrk;" * "\x27E6"- ,"LongLeftArrow;" * "\x27F5"- ,"longleftarrow;" * "\x27F5"- ,"Longleftarrow;" * "\x27F8"- ,"LongLeftRightArrow;" * "\x27F7"- ,"longleftrightarrow;" * "\x27F7"- ,"Longleftrightarrow;" * "\x27FA"- ,"longmapsto;" * "\x27FC"- ,"LongRightArrow;" * "\x27F6"- ,"longrightarrow;" * "\x27F6"- ,"Longrightarrow;" * "\x27F9"- ,"looparrowleft;" * "\x21AB"- ,"looparrowright;" * "\x21AC"- ,"lopar;" * "\x2985"- ,"Lopf;" * "\x1D543"- ,"lopf;" * "\x1D55D"- ,"loplus;" * "\x2A2D"- ,"lotimes;" * "\x2A34"- ,"lowast;" * "\x2217"- ,"lowbar;" * "\x005F"- ,"LowerLeftArrow;" * "\x2199"- ,"LowerRightArrow;" * "\x2198"- ,"loz;" * "\x25CA"- ,"lozenge;" * "\x25CA"- ,"lozf;" * "\x29EB"- ,"lpar;" * "\x0028"- ,"lparlt;" * "\x2993"- ,"lrarr;" * "\x21C6"- ,"lrcorner;" * "\x231F"- ,"lrhar;" * "\x21CB"- ,"lrhard;" * "\x296D"- ,"lrm;" * "\x200E"- ,"lrtri;" * "\x22BF"- ,"lsaquo;" * "\x2039"- ,"Lscr;" * "\x2112"- ,"lscr;" * "\x1D4C1"- ,"Lsh;" * "\x21B0"- ,"lsh;" * "\x21B0"- ,"lsim;" * "\x2272"- ,"lsime;" * "\x2A8D"- ,"lsimg;" * "\x2A8F"- ,"lsqb;" * "\x005B"- ,"lsquo;" * "\x2018"- ,"lsquor;" * "\x201A"- ,"Lstrok;" * "\x0141"- ,"lstrok;" * "\x0142"- ,"LT" * "\x003C"- ,"lt" * "\x003C"- ,"LT;" * "\x003C"- ,"lt;" * "\x003C"- ,"Lt;" * "\x226A"- ,"ltcc;" * "\x2AA6"- ,"ltcir;" * "\x2A79"- ,"ltdot;" * "\x22D6"- ,"lthree;" * "\x22CB"- ,"ltimes;" * "\x22C9"- ,"ltlarr;" * "\x2976"- ,"ltquest;" * "\x2A7B"- ,"ltri;" * "\x25C3"- ,"ltrie;" * "\x22B4"- ,"ltrif;" * "\x25C2"- ,"ltrPar;" * "\x2996"- ,"lurdshar;" * "\x294A"- ,"luruhar;" * "\x2966"- ,"lvertneqq;" * "\x2268\xFE00"- ,"lvnE;" * "\x2268\xFE00"- ,"macr" * "\x00AF"- ,"macr;" * "\x00AF"- ,"male;" * "\x2642"- ,"malt;" * "\x2720"- ,"maltese;" * "\x2720"- ,"map;" * "\x21A6"- ,"Map;" * "\x2905"- ,"mapsto;" * "\x21A6"- ,"mapstodown;" * "\x21A7"- ,"mapstoleft;" * "\x21A4"- ,"mapstoup;" * "\x21A5"- ,"marker;" * "\x25AE"- ,"mcomma;" * "\x2A29"- ,"Mcy;" * "\x041C"- ,"mcy;" * "\x043C"- ,"mdash;" * "\x2014"- ,"mDDot;" * "\x223A"- ,"measuredangle;" * "\x2221"- ,"MediumSpace;" * "\x205F"- ,"Mellintrf;" * "\x2133"- ,"Mfr;" * "\x1D510"- ,"mfr;" * "\x1D52A"- ,"mho;" * "\x2127"- ,"micro" * "\x00B5"- ,"micro;" * "\x00B5"- ,"mid;" * "\x2223"- ,"midast;" * "\x002A"- ,"midcir;" * "\x2AF0"- ,"middot" * "\x00B7"- ,"middot;" * "\x00B7"- ,"minus;" * "\x2212"- ,"minusb;" * "\x229F"- ,"minusd;" * "\x2238"- ,"minusdu;" * "\x2A2A"- ,"MinusPlus;" * "\x2213"- ,"mlcp;" * "\x2ADB"- ,"mldr;" * "\x2026"- ,"mnplus;" * "\x2213"- ,"models;" * "\x22A7"- ,"Mopf;" * "\x1D544"- ,"mopf;" * "\x1D55E"- ,"mp;" * "\x2213"- ,"Mscr;" * "\x2133"- ,"mscr;" * "\x1D4C2"- ,"mstpos;" * "\x223E"- ,"Mu;" * "\x039C"- ,"mu;" * "\x03BC"- ,"multimap;" * "\x22B8"- ,"mumap;" * "\x22B8"- ,"nabla;" * "\x2207"- ,"Nacute;" * "\x0143"- ,"nacute;" * "\x0144"- ,"nang;" * "\x2220\x20D2"- ,"nap;" * "\x2249"- ,"napE;" * "\x2A70\x0338"- ,"napid;" * "\x224B\x0338"- ,"napos;" * "\x0149"- ,"napprox;" * "\x2249"- ,"natur;" * "\x266E"- ,"natural;" * "\x266E"- ,"naturals;" * "\x2115"- ,"nbsp" * "\x00A0"- ,"nbsp;" * "\x00A0"- ,"nbump;" * "\x224E\x0338"- ,"nbumpe;" * "\x224F\x0338"- ,"ncap;" * "\x2A43"- ,"Ncaron;" * "\x0147"- ,"ncaron;" * "\x0148"- ,"Ncedil;" * "\x0145"- ,"ncedil;" * "\x0146"- ,"ncong;" * "\x2247"- ,"ncongdot;" * "\x2A6D\x0338"- ,"ncup;" * "\x2A42"- ,"Ncy;" * "\x041D"- ,"ncy;" * "\x043D"- ,"ndash;" * "\x2013"- ,"ne;" * "\x2260"- ,"nearhk;" * "\x2924"- ,"nearr;" * "\x2197"- ,"neArr;" * "\x21D7"- ,"nearrow;" * "\x2197"- ,"nedot;" * "\x2250\x0338"- ,"NegativeMediumSpace;" * "\x200B"- ,"NegativeThickSpace;" * "\x200B"- ,"NegativeThinSpace;" * "\x200B"- ,"NegativeVeryThinSpace;" * "\x200B"- ,"nequiv;" * "\x2262"- ,"nesear;" * "\x2928"- ,"nesim;" * "\x2242\x0338"- ,"NestedGreaterGreater;" * "\x226B"- ,"NestedLessLess;" * "\x226A"- ,"NewLine;" * "\x000A"- ,"nexist;" * "\x2204"- ,"nexists;" * "\x2204"- ,"Nfr;" * "\x1D511"- ,"nfr;" * "\x1D52B"- ,"ngE;" * "\x2267\x0338"- ,"nge;" * "\x2271"- ,"ngeq;" * "\x2271"- ,"ngeqq;" * "\x2267\x0338"- ,"ngeqslant;" * "\x2A7E\x0338"- ,"nges;" * "\x2A7E\x0338"- ,"nGg;" * "\x22D9\x0338"- ,"ngsim;" * "\x2275"- ,"nGt;" * "\x226B\x20D2"- ,"ngt;" * "\x226F"- ,"ngtr;" * "\x226F"- ,"nGtv;" * "\x226B\x0338"- ,"nharr;" * "\x21AE"- ,"nhArr;" * "\x21CE"- ,"nhpar;" * "\x2AF2"- ,"ni;" * "\x220B"- ,"nis;" * "\x22FC"- ,"nisd;" * "\x22FA"- ,"niv;" * "\x220B"- ,"NJcy;" * "\x040A"- ,"njcy;" * "\x045A"- ,"nlarr;" * "\x219A"- ,"nlArr;" * "\x21CD"- ,"nldr;" * "\x2025"- ,"nlE;" * "\x2266\x0338"- ,"nle;" * "\x2270"- ,"nleftarrow;" * "\x219A"- ,"nLeftarrow;" * "\x21CD"- ,"nleftrightarrow;" * "\x21AE"- ,"nLeftrightarrow;" * "\x21CE"- ,"nleq;" * "\x2270"- ,"nleqq;" * "\x2266\x0338"- ,"nleqslant;" * "\x2A7D\x0338"- ,"nles;" * "\x2A7D\x0338"- ,"nless;" * "\x226E"- ,"nLl;" * "\x22D8\x0338"- ,"nlsim;" * "\x2274"- ,"nLt;" * "\x226A\x20D2"- ,"nlt;" * "\x226E"- ,"nltri;" * "\x22EA"- ,"nltrie;" * "\x22EC"- ,"nLtv;" * "\x226A\x0338"- ,"nmid;" * "\x2224"- ,"NoBreak;" * "\x2060"- ,"NonBreakingSpace;" * "\x00A0"- ,"Nopf;" * "\x2115"- ,"nopf;" * "\x1D55F"- ,"not" * "\x00AC"- ,"not;" * "\x00AC"- ,"Not;" * "\x2AEC"- ,"NotCongruent;" * "\x2262"- ,"NotCupCap;" * "\x226D"- ,"NotDoubleVerticalBar;" * "\x2226"- ,"NotElement;" * "\x2209"- ,"NotEqual;" * "\x2260"- ,"NotEqualTilde;" * "\x2242\x0338"- ,"NotExists;" * "\x2204"- ,"NotGreater;" * "\x226F"- ,"NotGreaterEqual;" * "\x2271"- ,"NotGreaterFullEqual;" * "\x2267\x0338"- ,"NotGreaterGreater;" * "\x226B\x0338"- ,"NotGreaterLess;" * "\x2279"- ,"NotGreaterSlantEqual;" * "\x2A7E\x0338"- ,"NotGreaterTilde;" * "\x2275"- ,"NotHumpDownHump;" * "\x224E\x0338"- ,"NotHumpEqual;" * "\x224F\x0338"- ,"notin;" * "\x2209"- ,"notindot;" * "\x22F5\x0338"- ,"notinE;" * "\x22F9\x0338"- ,"notinva;" * "\x2209"- ,"notinvb;" * "\x22F7"- ,"notinvc;" * "\x22F6"- ,"NotLeftTriangle;" * "\x22EA"- ,"NotLeftTriangleBar;" * "\x29CF\x0338"- ,"NotLeftTriangleEqual;" * "\x22EC"- ,"NotLess;" * "\x226E"- ,"NotLessEqual;" * "\x2270"- ,"NotLessGreater;" * "\x2278"- ,"NotLessLess;" * "\x226A\x0338"- ,"NotLessSlantEqual;" * "\x2A7D\x0338"- ,"NotLessTilde;" * "\x2274"- ,"NotNestedGreaterGreater;" * "\x2AA2\x0338"- ,"NotNestedLessLess;" * "\x2AA1\x0338"- ,"notni;" * "\x220C"- ,"notniva;" * "\x220C"- ,"notnivb;" * "\x22FE"- ,"notnivc;" * "\x22FD"- ,"NotPrecedes;" * "\x2280"- ,"NotPrecedesEqual;" * "\x2AAF\x0338"- ,"NotPrecedesSlantEqual;" * "\x22E0"- ,"NotReverseElement;" * "\x220C"- ,"NotRightTriangle;" * "\x22EB"- ,"NotRightTriangleBar;" * "\x29D0\x0338"- ,"NotRightTriangleEqual;" * "\x22ED"- ,"NotSquareSubset;" * "\x228F\x0338"- ,"NotSquareSubsetEqual;" * "\x22E2"- ,"NotSquareSuperset;" * "\x2290\x0338"- ,"NotSquareSupersetEqual;" * "\x22E3"- ,"NotSubset;" * "\x2282\x20D2"- ,"NotSubsetEqual;" * "\x2288"- ,"NotSucceeds;" * "\x2281"- ,"NotSucceedsEqual;" * "\x2AB0\x0338"- ,"NotSucceedsSlantEqual;" * "\x22E1"- ,"NotSucceedsTilde;" * "\x227F\x0338"- ,"NotSuperset;" * "\x2283\x20D2"- ,"NotSupersetEqual;" * "\x2289"- ,"NotTilde;" * "\x2241"- ,"NotTildeEqual;" * "\x2244"- ,"NotTildeFullEqual;" * "\x2247"- ,"NotTildeTilde;" * "\x2249"- ,"NotVerticalBar;" * "\x2224"- ,"npar;" * "\x2226"- ,"nparallel;" * "\x2226"- ,"nparsl;" * "\x2AFD\x20E5"- ,"npart;" * "\x2202\x0338"- ,"npolint;" * "\x2A14"- ,"npr;" * "\x2280"- ,"nprcue;" * "\x22E0"- ,"npre;" * "\x2AAF\x0338"- ,"nprec;" * "\x2280"- ,"npreceq;" * "\x2AAF\x0338"- ,"nrarr;" * "\x219B"- ,"nrArr;" * "\x21CF"- ,"nrarrc;" * "\x2933\x0338"- ,"nrarrw;" * "\x219D\x0338"- ,"nrightarrow;" * "\x219B"- ,"nRightarrow;" * "\x21CF"- ,"nrtri;" * "\x22EB"- ,"nrtrie;" * "\x22ED"- ,"nsc;" * "\x2281"- ,"nsccue;" * "\x22E1"- ,"nsce;" * "\x2AB0\x0338"- ,"Nscr;" * "\x1D4A9"- ,"nscr;" * "\x1D4C3"- ,"nshortmid;" * "\x2224"- ,"nshortparallel;" * "\x2226"- ,"nsim;" * "\x2241"- ,"nsime;" * "\x2244"- ,"nsimeq;" * "\x2244"- ,"nsmid;" * "\x2224"- ,"nspar;" * "\x2226"- ,"nsqsube;" * "\x22E2"- ,"nsqsupe;" * "\x22E3"- ,"nsub;" * "\x2284"- ,"nsube;" * "\x2288"- ,"nsubE;" * "\x2AC5\x0338"- ,"nsubset;" * "\x2282\x20D2"- ,"nsubseteq;" * "\x2288"- ,"nsubseteqq;" * "\x2AC5\x0338"- ,"nsucc;" * "\x2281"- ,"nsucceq;" * "\x2AB0\x0338"- ,"nsup;" * "\x2285"- ,"nsupe;" * "\x2289"- ,"nsupE;" * "\x2AC6\x0338"- ,"nsupset;" * "\x2283\x20D2"- ,"nsupseteq;" * "\x2289"- ,"nsupseteqq;" * "\x2AC6\x0338"- ,"ntgl;" * "\x2279"- ,"Ntilde" * "\x00D1"- ,"ntilde" * "\x00F1"- ,"Ntilde;" * "\x00D1"- ,"ntilde;" * "\x00F1"- ,"ntlg;" * "\x2278"- ,"ntriangleleft;" * "\x22EA"- ,"ntrianglelefteq;" * "\x22EC"- ,"ntriangleright;" * "\x22EB"- ,"ntrianglerighteq;" * "\x22ED"- ,"Nu;" * "\x039D"- ,"nu;" * "\x03BD"- ,"num;" * "\x0023"- ,"numero;" * "\x2116"- ,"numsp;" * "\x2007"- ,"nvap;" * "\x224D\x20D2"- ,"nvdash;" * "\x22AC"- ,"nvDash;" * "\x22AD"- ,"nVdash;" * "\x22AE"- ,"nVDash;" * "\x22AF"- ,"nvge;" * "\x2265\x20D2"- ,"nvgt;" * "\x003E\x20D2"- ,"nvHarr;" * "\x2904"- ,"nvinfin;" * "\x29DE"- ,"nvlArr;" * "\x2902"- ,"nvle;" * "\x2264\x20D2"- ,"nvlt;" * "\x003C\x20D2"- ,"nvltrie;" * "\x22B4\x20D2"- ,"nvrArr;" * "\x2903"- ,"nvrtrie;" * "\x22B5\x20D2"- ,"nvsim;" * "\x223C\x20D2"- ,"nwarhk;" * "\x2923"- ,"nwarr;" * "\x2196"- ,"nwArr;" * "\x21D6"- ,"nwarrow;" * "\x2196"- ,"nwnear;" * "\x2927"- ,"Oacute" * "\x00D3"- ,"oacute" * "\x00F3"- ,"Oacute;" * "\x00D3"- ,"oacute;" * "\x00F3"- ,"oast;" * "\x229B"- ,"ocir;" * "\x229A"- ,"Ocirc" * "\x00D4"- ,"ocirc" * "\x00F4"- ,"Ocirc;" * "\x00D4"- ,"ocirc;" * "\x00F4"- ,"Ocy;" * "\x041E"- ,"ocy;" * "\x043E"- ,"odash;" * "\x229D"- ,"Odblac;" * "\x0150"- ,"odblac;" * "\x0151"- ,"odiv;" * "\x2A38"- ,"odot;" * "\x2299"- ,"odsold;" * "\x29BC"- ,"OElig;" * "\x0152"- ,"oelig;" * "\x0153"- ,"ofcir;" * "\x29BF"- ,"Ofr;" * "\x1D512"- ,"ofr;" * "\x1D52C"- ,"ogon;" * "\x02DB"- ,"Ograve" * "\x00D2"- ,"ograve" * "\x00F2"- ,"Ograve;" * "\x00D2"- ,"ograve;" * "\x00F2"- ,"ogt;" * "\x29C1"- ,"ohbar;" * "\x29B5"- ,"ohm;" * "\x03A9"- ,"oint;" * "\x222E"- ,"olarr;" * "\x21BA"- ,"olcir;" * "\x29BE"- ,"olcross;" * "\x29BB"- ,"oline;" * "\x203E"- ,"olt;" * "\x29C0"- ,"Omacr;" * "\x014C"- ,"omacr;" * "\x014D"- ,"Omega;" * "\x03A9"- ,"omega;" * "\x03C9"- ,"Omicron;" * "\x039F"- ,"omicron;" * "\x03BF"- ,"omid;" * "\x29B6"- ,"ominus;" * "\x2296"- ,"Oopf;" * "\x1D546"- ,"oopf;" * "\x1D560"- ,"opar;" * "\x29B7"- ,"OpenCurlyDoubleQuote;" * "\x201C"- ,"OpenCurlyQuote;" * "\x2018"- ,"operp;" * "\x29B9"- ,"oplus;" * "\x2295"- ,"or;" * "\x2228"- ,"Or;" * "\x2A54"- ,"orarr;" * "\x21BB"- ,"ord;" * "\x2A5D"- ,"order;" * "\x2134"- ,"orderof;" * "\x2134"- ,"ordf" * "\x00AA"- ,"ordf;" * "\x00AA"- ,"ordm" * "\x00BA"- ,"ordm;" * "\x00BA"- ,"origof;" * "\x22B6"- ,"oror;" * "\x2A56"- ,"orslope;" * "\x2A57"- ,"orv;" * "\x2A5B"- ,"oS;" * "\x24C8"- ,"oscr;" * "\x2134"- ,"Oscr;" * "\x1D4AA"- ,"Oslash" * "\x00D8"- ,"oslash" * "\x00F8"- ,"Oslash;" * "\x00D8"- ,"oslash;" * "\x00F8"- ,"osol;" * "\x2298"- ,"Otilde" * "\x00D5"- ,"otilde" * "\x00F5"- ,"Otilde;" * "\x00D5"- ,"otilde;" * "\x00F5"- ,"otimes;" * "\x2297"- ,"Otimes;" * "\x2A37"- ,"otimesas;" * "\x2A36"- ,"Ouml" * "\x00D6"- ,"ouml" * "\x00F6"- ,"Ouml;" * "\x00D6"- ,"ouml;" * "\x00F6"- ,"ovbar;" * "\x233D"- ,"OverBar;" * "\x203E"- ,"OverBrace;" * "\x23DE"- ,"OverBracket;" * "\x23B4"- ,"OverParenthesis;" * "\x23DC"- ,"par;" * "\x2225"- ,"para" * "\x00B6"- ,"para;" * "\x00B6"- ,"parallel;" * "\x2225"- ,"parsim;" * "\x2AF3"- ,"parsl;" * "\x2AFD"- ,"part;" * "\x2202"- ,"PartialD;" * "\x2202"- ,"Pcy;" * "\x041F"- ,"pcy;" * "\x043F"- ,"percnt;" * "\x0025"- ,"period;" * "\x002E"- ,"permil;" * "\x2030"- ,"perp;" * "\x22A5"- ,"pertenk;" * "\x2031"- ,"Pfr;" * "\x1D513"- ,"pfr;" * "\x1D52D"- ,"Phi;" * "\x03A6"- ,"phi;" * "\x03C6"- ,"phiv;" * "\x03D5"- ,"phmmat;" * "\x2133"- ,"phone;" * "\x260E"- ,"Pi;" * "\x03A0"- ,"pi;" * "\x03C0"- ,"pitchfork;" * "\x22D4"- ,"piv;" * "\x03D6"- ,"planck;" * "\x210F"- ,"planckh;" * "\x210E"- ,"plankv;" * "\x210F"- ,"plus;" * "\x002B"- ,"plusacir;" * "\x2A23"- ,"plusb;" * "\x229E"- ,"pluscir;" * "\x2A22"- ,"plusdo;" * "\x2214"- ,"plusdu;" * "\x2A25"- ,"pluse;" * "\x2A72"- ,"PlusMinus;" * "\x00B1"- ,"plusmn" * "\x00B1"- ,"plusmn;" * "\x00B1"- ,"plussim;" * "\x2A26"- ,"plustwo;" * "\x2A27"- ,"pm;" * "\x00B1"- ,"Poincareplane;" * "\x210C"- ,"pointint;" * "\x2A15"- ,"Popf;" * "\x2119"- ,"popf;" * "\x1D561"- ,"pound" * "\x00A3"- ,"pound;" * "\x00A3"- ,"pr;" * "\x227A"- ,"Pr;" * "\x2ABB"- ,"prap;" * "\x2AB7"- ,"prcue;" * "\x227C"- ,"pre;" * "\x2AAF"- ,"prE;" * "\x2AB3"- ,"prec;" * "\x227A"- ,"precapprox;" * "\x2AB7"- ,"preccurlyeq;" * "\x227C"- ,"Precedes;" * "\x227A"- ,"PrecedesEqual;" * "\x2AAF"- ,"PrecedesSlantEqual;" * "\x227C"- ,"PrecedesTilde;" * "\x227E"- ,"preceq;" * "\x2AAF"- ,"precnapprox;" * "\x2AB9"- ,"precneqq;" * "\x2AB5"- ,"precnsim;" * "\x22E8"- ,"precsim;" * "\x227E"- ,"prime;" * "\x2032"- ,"Prime;" * "\x2033"- ,"primes;" * "\x2119"- ,"prnap;" * "\x2AB9"- ,"prnE;" * "\x2AB5"- ,"prnsim;" * "\x22E8"- ,"prod;" * "\x220F"- ,"Product;" * "\x220F"- ,"profalar;" * "\x232E"- ,"profline;" * "\x2312"- ,"profsurf;" * "\x2313"- ,"prop;" * "\x221D"- ,"Proportion;" * "\x2237"- ,"Proportional;" * "\x221D"- ,"propto;" * "\x221D"- ,"prsim;" * "\x227E"- ,"prurel;" * "\x22B0"- ,"Pscr;" * "\x1D4AB"- ,"pscr;" * "\x1D4C5"- ,"Psi;" * "\x03A8"- ,"psi;" * "\x03C8"- ,"puncsp;" * "\x2008"- ,"Qfr;" * "\x1D514"- ,"qfr;" * "\x1D52E"- ,"qint;" * "\x2A0C"- ,"Qopf;" * "\x211A"- ,"qopf;" * "\x1D562"- ,"qprime;" * "\x2057"- ,"Qscr;" * "\x1D4AC"- ,"qscr;" * "\x1D4C6"- ,"quaternions;" * "\x210D"- ,"quatint;" * "\x2A16"- ,"quest;" * "\x003F"- ,"questeq;" * "\x225F"- ,"QUOT" * "\x0022"- ,"quot" * "\x0022"- ,"QUOT;" * "\x0022"- ,"quot;" * "\x0022"- ,"rAarr;" * "\x21DB"- ,"race;" * "\x223D\x0331"- ,"Racute;" * "\x0154"- ,"racute;" * "\x0155"- ,"radic;" * "\x221A"- ,"raemptyv;" * "\x29B3"- ,"rang;" * "\x27E9"- ,"Rang;" * "\x27EB"- ,"rangd;" * "\x2992"- ,"range;" * "\x29A5"- ,"rangle;" * "\x27E9"- ,"raquo" * "\x00BB"- ,"raquo;" * "\x00BB"- ,"rarr;" * "\x2192"- ,"Rarr;" * "\x21A0"- ,"rArr;" * "\x21D2"- ,"rarrap;" * "\x2975"- ,"rarrb;" * "\x21E5"- ,"rarrbfs;" * "\x2920"- ,"rarrc;" * "\x2933"- ,"rarrfs;" * "\x291E"- ,"rarrhk;" * "\x21AA"- ,"rarrlp;" * "\x21AC"- ,"rarrpl;" * "\x2945"- ,"rarrsim;" * "\x2974"- ,"rarrtl;" * "\x21A3"- ,"Rarrtl;" * "\x2916"- ,"rarrw;" * "\x219D"- ,"ratail;" * "\x291A"- ,"rAtail;" * "\x291C"- ,"ratio;" * "\x2236"- ,"rationals;" * "\x211A"- ,"rbarr;" * "\x290D"- ,"rBarr;" * "\x290F"- ,"RBarr;" * "\x2910"- ,"rbbrk;" * "\x2773"- ,"rbrace;" * "\x007D"- ,"rbrack;" * "\x005D"- ,"rbrke;" * "\x298C"- ,"rbrksld;" * "\x298E"- ,"rbrkslu;" * "\x2990"- ,"Rcaron;" * "\x0158"- ,"rcaron;" * "\x0159"- ,"Rcedil;" * "\x0156"- ,"rcedil;" * "\x0157"- ,"rceil;" * "\x2309"- ,"rcub;" * "\x007D"- ,"Rcy;" * "\x0420"- ,"rcy;" * "\x0440"- ,"rdca;" * "\x2937"- ,"rdldhar;" * "\x2969"- ,"rdquo;" * "\x201D"- ,"rdquor;" * "\x201D"- ,"rdsh;" * "\x21B3"- ,"Re;" * "\x211C"- ,"real;" * "\x211C"- ,"realine;" * "\x211B"- ,"realpart;" * "\x211C"- ,"reals;" * "\x211D"- ,"rect;" * "\x25AD"- ,"REG" * "\x00AE"- ,"reg" * "\x00AE"- ,"REG;" * "\x00AE"- ,"reg;" * "\x00AE"- ,"ReverseElement;" * "\x220B"- ,"ReverseEquilibrium;" * "\x21CB"- ,"ReverseUpEquilibrium;" * "\x296F"- ,"rfisht;" * "\x297D"- ,"rfloor;" * "\x230B"- ,"Rfr;" * "\x211C"- ,"rfr;" * "\x1D52F"- ,"rHar;" * "\x2964"- ,"rhard;" * "\x21C1"- ,"rharu;" * "\x21C0"- ,"rharul;" * "\x296C"- ,"Rho;" * "\x03A1"- ,"rho;" * "\x03C1"- ,"rhov;" * "\x03F1"- ,"RightAngleBracket;" * "\x27E9"- ,"RightArrow;" * "\x2192"- ,"rightarrow;" * "\x2192"- ,"Rightarrow;" * "\x21D2"- ,"RightArrowBar;" * "\x21E5"- ,"RightArrowLeftArrow;" * "\x21C4"- ,"rightarrowtail;" * "\x21A3"- ,"RightCeiling;" * "\x2309"- ,"RightDoubleBracket;" * "\x27E7"- ,"RightDownTeeVector;" * "\x295D"- ,"RightDownVector;" * "\x21C2"- ,"RightDownVectorBar;" * "\x2955"- ,"RightFloor;" * "\x230B"- ,"rightharpoondown;" * "\x21C1"- ,"rightharpoonup;" * "\x21C0"- ,"rightleftarrows;" * "\x21C4"- ,"rightleftharpoons;" * "\x21CC"- ,"rightrightarrows;" * "\x21C9"- ,"rightsquigarrow;" * "\x219D"- ,"RightTee;" * "\x22A2"- ,"RightTeeArrow;" * "\x21A6"- ,"RightTeeVector;" * "\x295B"- ,"rightthreetimes;" * "\x22CC"- ,"RightTriangle;" * "\x22B3"- ,"RightTriangleBar;" * "\x29D0"- ,"RightTriangleEqual;" * "\x22B5"- ,"RightUpDownVector;" * "\x294F"- ,"RightUpTeeVector;" * "\x295C"- ,"RightUpVector;" * "\x21BE"- ,"RightUpVectorBar;" * "\x2954"- ,"RightVector;" * "\x21C0"- ,"RightVectorBar;" * "\x2953"- ,"ring;" * "\x02DA"- ,"risingdotseq;" * "\x2253"- ,"rlarr;" * "\x21C4"- ,"rlhar;" * "\x21CC"- ,"rlm;" * "\x200F"- ,"rmoust;" * "\x23B1"- ,"rmoustache;" * "\x23B1"- ,"rnmid;" * "\x2AEE"- ,"roang;" * "\x27ED"- ,"roarr;" * "\x21FE"- ,"robrk;" * "\x27E7"- ,"ropar;" * "\x2986"- ,"Ropf;" * "\x211D"- ,"ropf;" * "\x1D563"- ,"roplus;" * "\x2A2E"- ,"rotimes;" * "\x2A35"- ,"RoundImplies;" * "\x2970"- ,"rpar;" * "\x0029"- ,"rpargt;" * "\x2994"- ,"rppolint;" * "\x2A12"- ,"rrarr;" * "\x21C9"- ,"Rrightarrow;" * "\x21DB"- ,"rsaquo;" * "\x203A"- ,"Rscr;" * "\x211B"- ,"rscr;" * "\x1D4C7"- ,"Rsh;" * "\x21B1"- ,"rsh;" * "\x21B1"- ,"rsqb;" * "\x005D"- ,"rsquo;" * "\x2019"- ,"rsquor;" * "\x2019"- ,"rthree;" * "\x22CC"- ,"rtimes;" * "\x22CA"- ,"rtri;" * "\x25B9"- ,"rtrie;" * "\x22B5"- ,"rtrif;" * "\x25B8"- ,"rtriltri;" * "\x29CE"- ,"RuleDelayed;" * "\x29F4"- ,"ruluhar;" * "\x2968"- ,"rx;" * "\x211E"- ,"Sacute;" * "\x015A"- ,"sacute;" * "\x015B"- ,"sbquo;" * "\x201A"- ,"sc;" * "\x227B"- ,"Sc;" * "\x2ABC"- ,"scap;" * "\x2AB8"- ,"Scaron;" * "\x0160"- ,"scaron;" * "\x0161"- ,"sccue;" * "\x227D"- ,"sce;" * "\x2AB0"- ,"scE;" * "\x2AB4"- ,"Scedil;" * "\x015E"- ,"scedil;" * "\x015F"- ,"Scirc;" * "\x015C"- ,"scirc;" * "\x015D"- ,"scnap;" * "\x2ABA"- ,"scnE;" * "\x2AB6"- ,"scnsim;" * "\x22E9"- ,"scpolint;" * "\x2A13"- ,"scsim;" * "\x227F"- ,"Scy;" * "\x0421"- ,"scy;" * "\x0441"- ,"sdot;" * "\x22C5"- ,"sdotb;" * "\x22A1"- ,"sdote;" * "\x2A66"- ,"searhk;" * "\x2925"- ,"searr;" * "\x2198"- ,"seArr;" * "\x21D8"- ,"searrow;" * "\x2198"- ,"sect" * "\x00A7"- ,"sect;" * "\x00A7"- ,"semi;" * "\x003B"- ,"seswar;" * "\x2929"- ,"setminus;" * "\x2216"- ,"setmn;" * "\x2216"- ,"sext;" * "\x2736"- ,"Sfr;" * "\x1D516"- ,"sfr;" * "\x1D530"- ,"sfrown;" * "\x2322"- ,"sharp;" * "\x266F"- ,"SHCHcy;" * "\x0429"- ,"shchcy;" * "\x0449"- ,"SHcy;" * "\x0428"- ,"shcy;" * "\x0448"- ,"ShortDownArrow;" * "\x2193"- ,"ShortLeftArrow;" * "\x2190"- ,"shortmid;" * "\x2223"- ,"shortparallel;" * "\x2225"- ,"ShortRightArrow;" * "\x2192"- ,"ShortUpArrow;" * "\x2191"- ,"shy" * "\x00AD"- ,"shy;" * "\x00AD"- ,"Sigma;" * "\x03A3"- ,"sigma;" * "\x03C3"- ,"sigmaf;" * "\x03C2"- ,"sigmav;" * "\x03C2"- ,"sim;" * "\x223C"- ,"simdot;" * "\x2A6A"- ,"sime;" * "\x2243"- ,"simeq;" * "\x2243"- ,"simg;" * "\x2A9E"- ,"simgE;" * "\x2AA0"- ,"siml;" * "\x2A9D"- ,"simlE;" * "\x2A9F"- ,"simne;" * "\x2246"- ,"simplus;" * "\x2A24"- ,"simrarr;" * "\x2972"- ,"slarr;" * "\x2190"- ,"SmallCircle;" * "\x2218"- ,"smallsetminus;" * "\x2216"- ,"smashp;" * "\x2A33"- ,"smeparsl;" * "\x29E4"- ,"smid;" * "\x2223"- ,"smile;" * "\x2323"- ,"smt;" * "\x2AAA"- ,"smte;" * "\x2AAC"- ,"smtes;" * "\x2AAC\xFE00"- ,"SOFTcy;" * "\x042C"- ,"softcy;" * "\x044C"- ,"sol;" * "\x002F"- ,"solb;" * "\x29C4"- ,"solbar;" * "\x233F"- ,"Sopf;" * "\x1D54A"- ,"sopf;" * "\x1D564"- ,"spades;" * "\x2660"- ,"spadesuit;" * "\x2660"- ,"spar;" * "\x2225"- ,"sqcap;" * "\x2293"- ,"sqcaps;" * "\x2293\xFE00"- ,"sqcup;" * "\x2294"- ,"sqcups;" * "\x2294\xFE00"- ,"Sqrt;" * "\x221A"- ,"sqsub;" * "\x228F"- ,"sqsube;" * "\x2291"- ,"sqsubset;" * "\x228F"- ,"sqsubseteq;" * "\x2291"- ,"sqsup;" * "\x2290"- ,"sqsupe;" * "\x2292"- ,"sqsupset;" * "\x2290"- ,"sqsupseteq;" * "\x2292"- ,"squ;" * "\x25A1"- ,"Square;" * "\x25A1"- ,"square;" * "\x25A1"- ,"SquareIntersection;" * "\x2293"- ,"SquareSubset;" * "\x228F"- ,"SquareSubsetEqual;" * "\x2291"- ,"SquareSuperset;" * "\x2290"- ,"SquareSupersetEqual;" * "\x2292"- ,"SquareUnion;" * "\x2294"- ,"squarf;" * "\x25AA"- ,"squf;" * "\x25AA"- ,"srarr;" * "\x2192"- ,"Sscr;" * "\x1D4AE"- ,"sscr;" * "\x1D4C8"- ,"ssetmn;" * "\x2216"- ,"ssmile;" * "\x2323"- ,"sstarf;" * "\x22C6"- ,"Star;" * "\x22C6"- ,"star;" * "\x2606"- ,"starf;" * "\x2605"- ,"straightepsilon;" * "\x03F5"- ,"straightphi;" * "\x03D5"- ,"strns;" * "\x00AF"- ,"sub;" * "\x2282"- ,"Sub;" * "\x22D0"- ,"subdot;" * "\x2ABD"- ,"sube;" * "\x2286"- ,"subE;" * "\x2AC5"- ,"subedot;" * "\x2AC3"- ,"submult;" * "\x2AC1"- ,"subne;" * "\x228A"- ,"subnE;" * "\x2ACB"- ,"subplus;" * "\x2ABF"- ,"subrarr;" * "\x2979"- ,"subset;" * "\x2282"- ,"Subset;" * "\x22D0"- ,"subseteq;" * "\x2286"- ,"subseteqq;" * "\x2AC5"- ,"SubsetEqual;" * "\x2286"- ,"subsetneq;" * "\x228A"- ,"subsetneqq;" * "\x2ACB"- ,"subsim;" * "\x2AC7"- ,"subsub;" * "\x2AD5"- ,"subsup;" * "\x2AD3"- ,"succ;" * "\x227B"- ,"succapprox;" * "\x2AB8"- ,"succcurlyeq;" * "\x227D"- ,"Succeeds;" * "\x227B"- ,"SucceedsEqual;" * "\x2AB0"- ,"SucceedsSlantEqual;" * "\x227D"- ,"SucceedsTilde;" * "\x227F"- ,"succeq;" * "\x2AB0"- ,"succnapprox;" * "\x2ABA"- ,"succneqq;" * "\x2AB6"- ,"succnsim;" * "\x22E9"- ,"succsim;" * "\x227F"- ,"SuchThat;" * "\x220B"- ,"Sum;" * "\x2211"- ,"sum;" * "\x2211"- ,"sung;" * "\x266A"- ,"sup1" * "\x00B9"- ,"sup1;" * "\x00B9"- ,"sup2" * "\x00B2"- ,"sup2;" * "\x00B2"- ,"sup3" * "\x00B3"- ,"sup3;" * "\x00B3"- ,"sup;" * "\x2283"- ,"Sup;" * "\x22D1"- ,"supdot;" * "\x2ABE"- ,"supdsub;" * "\x2AD8"- ,"supe;" * "\x2287"- ,"supE;" * "\x2AC6"- ,"supedot;" * "\x2AC4"- ,"Superset;" * "\x2283"- ,"SupersetEqual;" * "\x2287"- ,"suphsol;" * "\x27C9"- ,"suphsub;" * "\x2AD7"- ,"suplarr;" * "\x297B"- ,"supmult;" * "\x2AC2"- ,"supne;" * "\x228B"- ,"supnE;" * "\x2ACC"- ,"supplus;" * "\x2AC0"- ,"supset;" * "\x2283"- ,"Supset;" * "\x22D1"- ,"supseteq;" * "\x2287"- ,"supseteqq;" * "\x2AC6"- ,"supsetneq;" * "\x228B"- ,"supsetneqq;" * "\x2ACC"- ,"supsim;" * "\x2AC8"- ,"supsub;" * "\x2AD4"- ,"supsup;" * "\x2AD6"- ,"swarhk;" * "\x2926"- ,"swarr;" * "\x2199"- ,"swArr;" * "\x21D9"- ,"swarrow;" * "\x2199"- ,"swnwar;" * "\x292A"- ,"szlig" * "\x00DF"- ,"szlig;" * "\x00DF"- ,"Tab;" * "\x0009"- ,"target;" * "\x2316"- ,"Tau;" * "\x03A4"- ,"tau;" * "\x03C4"- ,"tbrk;" * "\x23B4"- ,"Tcaron;" * "\x0164"- ,"tcaron;" * "\x0165"- ,"Tcedil;" * "\x0162"- ,"tcedil;" * "\x0163"- ,"Tcy;" * "\x0422"- ,"tcy;" * "\x0442"- ,"tdot;" * "\x20DB"- ,"telrec;" * "\x2315"- ,"Tfr;" * "\x1D517"- ,"tfr;" * "\x1D531"- ,"there4;" * "\x2234"- ,"Therefore;" * "\x2234"- ,"therefore;" * "\x2234"- ,"Theta;" * "\x0398"- ,"theta;" * "\x03B8"- ,"thetasym;" * "\x03D1"- ,"thetav;" * "\x03D1"- ,"thickapprox;" * "\x2248"- ,"thicksim;" * "\x223C"- ,"ThickSpace;" * "\x205F\x200A"- ,"thinsp;" * "\x2009"- ,"ThinSpace;" * "\x2009"- ,"thkap;" * "\x2248"- ,"thksim;" * "\x223C"- ,"THORN" * "\x00DE"- ,"thorn" * "\x00FE"- ,"THORN;" * "\x00DE"- ,"thorn;" * "\x00FE"- ,"tilde;" * "\x02DC"- ,"Tilde;" * "\x223C"- ,"TildeEqual;" * "\x2243"- ,"TildeFullEqual;" * "\x2245"- ,"TildeTilde;" * "\x2248"- ,"times" * "\x00D7"- ,"times;" * "\x00D7"- ,"timesb;" * "\x22A0"- ,"timesbar;" * "\x2A31"- ,"timesd;" * "\x2A30"- ,"tint;" * "\x222D"- ,"toea;" * "\x2928"- ,"top;" * "\x22A4"- ,"topbot;" * "\x2336"- ,"topcir;" * "\x2AF1"- ,"Topf;" * "\x1D54B"- ,"topf;" * "\x1D565"- ,"topfork;" * "\x2ADA"- ,"tosa;" * "\x2929"- ,"tprime;" * "\x2034"- ,"TRADE;" * "\x2122"- ,"trade;" * "\x2122"- ,"triangle;" * "\x25B5"- ,"triangledown;" * "\x25BF"- ,"triangleleft;" * "\x25C3"- ,"trianglelefteq;" * "\x22B4"- ,"triangleq;" * "\x225C"- ,"triangleright;" * "\x25B9"- ,"trianglerighteq;" * "\x22B5"- ,"tridot;" * "\x25EC"- ,"trie;" * "\x225C"- ,"triminus;" * "\x2A3A"- ,"TripleDot;" * "\x20DB"- ,"triplus;" * "\x2A39"- ,"trisb;" * "\x29CD"- ,"tritime;" * "\x2A3B"- ,"trpezium;" * "\x23E2"- ,"Tscr;" * "\x1D4AF"- ,"tscr;" * "\x1D4C9"- ,"TScy;" * "\x0426"- ,"tscy;" * "\x0446"- ,"TSHcy;" * "\x040B"- ,"tshcy;" * "\x045B"- ,"Tstrok;" * "\x0166"- ,"tstrok;" * "\x0167"- ,"twixt;" * "\x226C"- ,"twoheadleftarrow;" * "\x219E"- ,"twoheadrightarrow;" * "\x21A0"- ,"Uacute" * "\x00DA"- ,"uacute" * "\x00FA"- ,"Uacute;" * "\x00DA"- ,"uacute;" * "\x00FA"- ,"uarr;" * "\x2191"- ,"Uarr;" * "\x219F"- ,"uArr;" * "\x21D1"- ,"Uarrocir;" * "\x2949"- ,"Ubrcy;" * "\x040E"- ,"ubrcy;" * "\x045E"- ,"Ubreve;" * "\x016C"- ,"ubreve;" * "\x016D"- ,"Ucirc" * "\x00DB"- ,"ucirc" * "\x00FB"- ,"Ucirc;" * "\x00DB"- ,"ucirc;" * "\x00FB"- ,"Ucy;" * "\x0423"- ,"ucy;" * "\x0443"- ,"udarr;" * "\x21C5"- ,"Udblac;" * "\x0170"- ,"udblac;" * "\x0171"- ,"udhar;" * "\x296E"- ,"ufisht;" * "\x297E"- ,"Ufr;" * "\x1D518"- ,"ufr;" * "\x1D532"- ,"Ugrave" * "\x00D9"- ,"ugrave" * "\x00F9"- ,"Ugrave;" * "\x00D9"- ,"ugrave;" * "\x00F9"- ,"uHar;" * "\x2963"- ,"uharl;" * "\x21BF"- ,"uharr;" * "\x21BE"- ,"uhblk;" * "\x2580"- ,"ulcorn;" * "\x231C"- ,"ulcorner;" * "\x231C"- ,"ulcrop;" * "\x230F"- ,"ultri;" * "\x25F8"- ,"Umacr;" * "\x016A"- ,"umacr;" * "\x016B"- ,"uml" * "\x00A8"- ,"uml;" * "\x00A8"- ,"UnderBar;" * "\x005F"- ,"UnderBrace;" * "\x23DF"- ,"UnderBracket;" * "\x23B5"- ,"UnderParenthesis;" * "\x23DD"- ,"Union;" * "\x22C3"- ,"UnionPlus;" * "\x228E"- ,"Uogon;" * "\x0172"- ,"uogon;" * "\x0173"- ,"Uopf;" * "\x1D54C"- ,"uopf;" * "\x1D566"- ,"UpArrow;" * "\x2191"- ,"uparrow;" * "\x2191"- ,"Uparrow;" * "\x21D1"- ,"UpArrowBar;" * "\x2912"- ,"UpArrowDownArrow;" * "\x21C5"- ,"UpDownArrow;" * "\x2195"- ,"updownarrow;" * "\x2195"- ,"Updownarrow;" * "\x21D5"- ,"UpEquilibrium;" * "\x296E"- ,"upharpoonleft;" * "\x21BF"- ,"upharpoonright;" * "\x21BE"- ,"uplus;" * "\x228E"- ,"UpperLeftArrow;" * "\x2196"- ,"UpperRightArrow;" * "\x2197"- ,"upsi;" * "\x03C5"- ,"Upsi;" * "\x03D2"- ,"upsih;" * "\x03D2"- ,"Upsilon;" * "\x03A5"- ,"upsilon;" * "\x03C5"- ,"UpTee;" * "\x22A5"- ,"UpTeeArrow;" * "\x21A5"- ,"upuparrows;" * "\x21C8"- ,"urcorn;" * "\x231D"- ,"urcorner;" * "\x231D"- ,"urcrop;" * "\x230E"- ,"Uring;" * "\x016E"- ,"uring;" * "\x016F"- ,"urtri;" * "\x25F9"- ,"Uscr;" * "\x1D4B0"- ,"uscr;" * "\x1D4CA"- ,"utdot;" * "\x22F0"- ,"Utilde;" * "\x0168"- ,"utilde;" * "\x0169"- ,"utri;" * "\x25B5"- ,"utrif;" * "\x25B4"- ,"uuarr;" * "\x21C8"- ,"Uuml" * "\x00DC"- ,"uuml" * "\x00FC"- ,"Uuml;" * "\x00DC"- ,"uuml;" * "\x00FC"- ,"uwangle;" * "\x29A7"- ,"vangrt;" * "\x299C"- ,"varepsilon;" * "\x03F5"- ,"varkappa;" * "\x03F0"- ,"varnothing;" * "\x2205"- ,"varphi;" * "\x03D5"- ,"varpi;" * "\x03D6"- ,"varpropto;" * "\x221D"- ,"varr;" * "\x2195"- ,"vArr;" * "\x21D5"- ,"varrho;" * "\x03F1"- ,"varsigma;" * "\x03C2"- ,"varsubsetneq;" * "\x228A\xFE00"- ,"varsubsetneqq;" * "\x2ACB\xFE00"- ,"varsupsetneq;" * "\x228B\xFE00"- ,"varsupsetneqq;" * "\x2ACC\xFE00"- ,"vartheta;" * "\x03D1"- ,"vartriangleleft;" * "\x22B2"- ,"vartriangleright;" * "\x22B3"- ,"vBar;" * "\x2AE8"- ,"Vbar;" * "\x2AEB"- ,"vBarv;" * "\x2AE9"- ,"Vcy;" * "\x0412"- ,"vcy;" * "\x0432"- ,"vdash;" * "\x22A2"- ,"vDash;" * "\x22A8"- ,"Vdash;" * "\x22A9"- ,"VDash;" * "\x22AB"- ,"Vdashl;" * "\x2AE6"- ,"vee;" * "\x2228"- ,"Vee;" * "\x22C1"- ,"veebar;" * "\x22BB"- ,"veeeq;" * "\x225A"- ,"vellip;" * "\x22EE"- ,"verbar;" * "\x007C"- ,"Verbar;" * "\x2016"- ,"vert;" * "\x007C"- ,"Vert;" * "\x2016"- ,"VerticalBar;" * "\x2223"- ,"VerticalLine;" * "\x007C"- ,"VerticalSeparator;" * "\x2758"- ,"VerticalTilde;" * "\x2240"- ,"VeryThinSpace;" * "\x200A"- ,"Vfr;" * "\x1D519"- ,"vfr;" * "\x1D533"- ,"vltri;" * "\x22B2"- ,"vnsub;" * "\x2282\x20D2"- ,"vnsup;" * "\x2283\x20D2"- ,"Vopf;" * "\x1D54D"- ,"vopf;" * "\x1D567"- ,"vprop;" * "\x221D"- ,"vrtri;" * "\x22B3"- ,"Vscr;" * "\x1D4B1"- ,"vscr;" * "\x1D4CB"- ,"vsubne;" * "\x228A\xFE00"- ,"vsubnE;" * "\x2ACB\xFE00"- ,"vsupne;" * "\x228B\xFE00"- ,"vsupnE;" * "\x2ACC\xFE00"- ,"Vvdash;" * "\x22AA"- ,"vzigzag;" * "\x299A"- ,"Wcirc;" * "\x0174"- ,"wcirc;" * "\x0175"- ,"wedbar;" * "\x2A5F"- ,"wedge;" * "\x2227"- ,"Wedge;" * "\x22C0"- ,"wedgeq;" * "\x2259"- ,"weierp;" * "\x2118"- ,"Wfr;" * "\x1D51A"- ,"wfr;" * "\x1D534"- ,"Wopf;" * "\x1D54E"- ,"wopf;" * "\x1D568"- ,"wp;" * "\x2118"- ,"wr;" * "\x2240"- ,"wreath;" * "\x2240"- ,"Wscr;" * "\x1D4B2"- ,"wscr;" * "\x1D4CC"- ,"xcap;" * "\x22C2"- ,"xcirc;" * "\x25EF"- ,"xcup;" * "\x22C3"- ,"xdtri;" * "\x25BD"- ,"Xfr;" * "\x1D51B"- ,"xfr;" * "\x1D535"- ,"xharr;" * "\x27F7"- ,"xhArr;" * "\x27FA"- ,"Xi;" * "\x039E"- ,"xi;" * "\x03BE"- ,"xlarr;" * "\x27F5"- ,"xlArr;" * "\x27F8"- ,"xmap;" * "\x27FC"- ,"xnis;" * "\x22FB"- ,"xodot;" * "\x2A00"- ,"Xopf;" * "\x1D54F"- ,"xopf;" * "\x1D569"- ,"xoplus;" * "\x2A01"- ,"xotime;" * "\x2A02"- ,"xrarr;" * "\x27F6"- ,"xrArr;" * "\x27F9"- ,"Xscr;" * "\x1D4B3"- ,"xscr;" * "\x1D4CD"- ,"xsqcup;" * "\x2A06"- ,"xuplus;" * "\x2A04"- ,"xutri;" * "\x25B3"- ,"xvee;" * "\x22C1"- ,"xwedge;" * "\x22C0"- ,"Yacute" * "\x00DD"- ,"yacute" * "\x00FD"- ,"Yacute;" * "\x00DD"- ,"yacute;" * "\x00FD"- ,"YAcy;" * "\x042F"- ,"yacy;" * "\x044F"- ,"Ycirc;" * "\x0176"- ,"ycirc;" * "\x0177"- ,"Ycy;" * "\x042B"- ,"ycy;" * "\x044B"- ,"yen" * "\x00A5"- ,"yen;" * "\x00A5"- ,"Yfr;" * "\x1D51C"- ,"yfr;" * "\x1D536"- ,"YIcy;" * "\x0407"- ,"yicy;" * "\x0457"- ,"Yopf;" * "\x1D550"- ,"yopf;" * "\x1D56A"- ,"Yscr;" * "\x1D4B4"- ,"yscr;" * "\x1D4CE"- ,"YUcy;" * "\x042E"- ,"yucy;" * "\x044E"- ,"yuml" * "\x00FF"- ,"yuml;" * "\x00FF"- ,"Yuml;" * "\x0178"- ,"Zacute;" * "\x0179"- ,"zacute;" * "\x017A"- ,"Zcaron;" * "\x017D"- ,"zcaron;" * "\x017E"- ,"Zcy;" * "\x0417"- ,"zcy;" * "\x0437"- ,"Zdot;" * "\x017B"- ,"zdot;" * "\x017C"- ,"zeetrf;" * "\x2128"- ,"ZeroWidthSpace;" * "\x200B"- ,"Zeta;" * "\x0396"- ,"zeta;" * "\x03B6"- ,"Zfr;" * "\x2128"- ,"zfr;" * "\x1D537"- ,"ZHcy;" * "\x0416"- ,"zhcy;" * "\x0436"- ,"zigrarr;" * "\x21DD"- ,"Zopf;" * "\x2124"- ,"zopf;" * "\x1D56B"- ,"Zscr;" * "\x1D4B5"- ,"zscr;" * "\x1D4CF"- ,"zwj;" * "\x200D"- ,"zwnj;" * "\x200C"- ]
− Text/HTML/TagSoup/Generated.hs
@@ -1,2 +0,0 @@-module Text.HTML.TagSoup.Generated(parseTagsOptions) where-import Text.HTML.TagSoup.Manual
− Text/HTML/TagSoup/Implementation.hs
@@ -1,193 +0,0 @@-{-# LANGUAGE RecordWildCards, PatternGuards, ScopedTypeVariables #-}--module Text.HTML.TagSoup.Implementation where--import Text.HTML.TagSoup.Type-import Text.HTML.TagSoup.Options-import Text.StringLike as Str-import Numeric (readHex)-import Data.Char (chr, ord)-import Data.Ix-import Control.Exception(assert)-import Control.Arrow-------------------------------------------------------------------------- BOTTOM LAYER--data Out- = Char Char- | Tag -- <- | TagShut -- </- | AttName- | AttVal- | TagEnd -- >- | TagEndClose -- />- | Comment -- <!--- | CommentEnd -- -->- | EntityName -- &- | EntityNum -- &#- | EntityHex -- &#x- | EntityEnd Bool -- Attributed followed by ; for True, missing ; for False- | Warn String- | Pos Position- deriving (Show,Eq)--errSeen x = Warn $ "Unexpected " ++ show x-errWant x = Warn $ "Expected " ++ show x--data S = S- {s :: S- ,tl :: S- ,hd :: Char- ,eof :: Bool- ,next :: String -> Maybe S- ,pos :: [Out] -> [Out]- }---expand :: Position -> String -> S-expand p text = p `seq` res- where res = S{s = res- ,tl = expand (positionChar p (head text)) (tail text)- ,hd = if null text then '\0' else head text- ,eof = null text- ,next = next p text- ,pos = (Pos p:)- }-- next p (t:ext) (s:tr) | t == s = next (positionChar p t) ext tr- next p text [] = Just $ expand p text- next _ _ _ = Nothing---infixr &--class Outable a where (&) :: a -> [Out] -> [Out]-instance Outable Char where (&) = ampChar-instance Outable Out where (&) = ampOut-ampChar x y = Char x : y-ampOut x y = x : y---state :: String -> S-state s = expand nullPosition s-------------------------------------------------------------------------- TOP LAYER---output :: forall str . StringLike str => ParseOptions str -> [Out] -> [Tag str]-output ParseOptions{..} x = (if optTagTextMerge then tagTextMerge else id) $ go ((nullPosition,[]),x)- where- -- main choice loop- go :: ((Position,[Tag str]),[Out]) -> [Tag str]- go ((p,ws),xs) | p `seq` False = [] -- otherwise p is a space leak when optTagPosition == False- go ((p,ws),xs) | not $ null ws = (if optTagWarning then (reverse ws++) else id) $ go ((p,[]),xs)- go ((p,ws),Pos p2:xs) = go ((p2,ws),xs)-- go x | isChar x = pos x $ TagText a : go y- where (y,a) = charsStr x- go x | isTag x = pos x $ TagOpen a b : (if isTagEndClose z then pos x $ TagClose a : go (next z) else go (skip isTagEnd z))- where (y,a) = charsStr $ next x- (z,b) = atts y- go x | isTagShut x = pos x $ (TagClose a:) $- (if not (null b) then warn x "Unexpected attributes in close tag" else id) $- if isTagEndClose z then warn x "Unexpected self-closing in close tag" $ go (next z) else go (skip isTagEnd z)- where (y,a) = charsStr $ next x- (z,b) = atts y- go x | isComment x = pos x $ TagComment a : go (skip isCommentEnd y)- where (y,a) = charsStr $ next x- go x | isEntityName x = poss x ((if optTagWarning then id else filter (not . isTagWarning)) $ optEntityData (a, getEntityEnd y)) ++ go (skip isEntityEnd y) - where (y,a) = charsStr $ next x- go x | isEntityNumHex x = pos x $ TagText (fromChar $ entityChr x a) : go (skip isEntityEnd y)- where (y,a) = chars $ next x- go x | Just a <- fromWarn x = if optTagWarning then pos x $ TagWarning (fromString a) : go (next x) else go (next x)- go x | isEof x = []-- atts :: ((Position,[Tag str]),[Out]) -> ( ((Position,[Tag str]),[Out]) , [(str,str)] )- atts x | isAttName x = second ((a,b):) $ atts z- where (y,a) = charsStr (next x)- (z,b) = if isAttVal y then charsEntsStr (next y) else (y, empty)- atts x | isAttVal x = second ((empty,a):) $ atts y- where (y,a) = charsEntsStr (next x)- atts x = (x, [])-- -- chars- chars x = charss False x- charsStr x = (id *** fromString) $ chars x- charsEntsStr x = (id *** fromString) $ charss True x-- -- loop round collecting characters, if the b is set including entity- charss :: Bool -> ((Position,[Tag str]),[Out]) -> ( ((Position,[Tag str]),[Out]) , String)- charss t x | Just a <- fromChr x = (y, a:b)- where (y,b) = charss t (next x)- charss t x | t, isEntityName x = second (toString n ++) $ charss t $ addWarns m z- where (y,a) = charsStr $ next x- b = getEntityEnd y- z = skip isEntityEnd y- (n,m) = optEntityAttrib (a,b)- charss t x | t, isEntityNumHex x = second (entityChr x a:) $ charss t z- where (y,a) = chars $ next x- z = skip isEntityEnd y- charss t ((_,w),Pos p:xs) = charss t ((p,w),xs)- charss t x | Just a <- fromWarn x = charss t $ (if optTagWarning then addWarns [TagWarning $ fromString a] else id) $ next x- charss t x = (x, [])-- -- utility functions- next x = second (drop 1) x- skip f x = assert (isEof x || f x) (next x)- addWarns ws x@((p,w),y) = ((p, reverse (poss x ws) ++ w), y)- pos ((p,_),_) rest = if optTagPosition then tagPosition p : rest else rest- warn x s rest = if optTagWarning then pos x $ TagWarning (fromString s) : rest else rest- poss x = concatMap (\w -> pos x [w]) ---entityChr x s | isEntityNum x = chr_ $ read s- | isEntityHex x = chr_ $ fst $ head $ readHex s- where chr_ x | inRange (toInteger $ ord minBound, toInteger $ ord maxBound) x = chr $ fromInteger x- | otherwise = '?'---isEof (_,[]) = True; isEof _ = False-isChar (_,Char{}:_) = True; isChar _ = False-isTag (_,Tag{}:_) = True; isTag _ = False-isTagShut (_,TagShut{}:_) = True; isTagShut _ = False-isAttName (_,AttName{}:_) = True; isAttName _ = False-isAttVal (_,AttVal{}:_) = True; isAttVal _ = False-isTagEnd (_,TagEnd{}:_) = True; isTagEnd _ = False-isTagEndClose (_,TagEndClose{}:_) = True; isTagEndClose _ = False-isComment (_,Comment{}:_) = True; isComment _ = False-isCommentEnd (_,CommentEnd{}:_) = True; isCommentEnd _ = False-isEntityName (_,EntityName{}:_) = True; isEntityName _ = False-isEntityNumHex (_,EntityNum{}:_) = True; isEntityNumHex (_,EntityHex{}:_) = True; isEntityNumHex _ = False-isEntityNum (_,EntityNum{}:_) = True; isEntityNum _ = False-isEntityHex (_,EntityHex{}:_) = True; isEntityHex _ = False-isEntityEnd (_,EntityEnd{}:_) = True; isEntityEnd _ = False-isWarn (_,Warn{}:_) = True; isWarn _ = False--fromChr (_,Char x:_) = Just x ; fromChr _ = Nothing-fromWarn (_,Warn x:_) = Just x ; fromWarn _ = Nothing--getEntityEnd (_,EntityEnd b:_) = b----- Merge all adjacent TagText bits-tagTextMerge :: StringLike str => [Tag str] -> [Tag str]-tagTextMerge (TagText x:xs) = TagText (strConcat (x:a)) : tagTextMerge b- where- (a,b) = f xs-- -- additional brackets on 3 lines to work around HSE 1.3.2 bugs with pattern fixities- f (TagText x:xs) = (x:a,b)- where (a,b) = f xs- f (TagPosition{}:(x@TagText{}:xs)) = f $ x : xs- f x = g x id x-- g o op (p@TagPosition{}:(w@TagWarning{}:xs)) = g o (op . (p:) . (w:)) xs- g o op (w@TagWarning{}:xs) = g o (op . (w:)) xs- g o op (p@TagPosition{}:(x@TagText{}:xs)) = f $ p : x : op xs- g o op (x@TagText{}:xs) = f $ x : op xs- g o op _ = ([], o)--tagTextMerge (x:xs) = x : tagTextMerge xs-tagTextMerge [] = []
− Text/HTML/TagSoup/Manual.hs
@@ -1,13 +0,0 @@--module Text.HTML.TagSoup.Manual(parseTagsOptions) where--import Text.HTML.TagSoup.Specification-import Text.HTML.TagSoup.Implementation-import Text.HTML.TagSoup.Type-import Text.HTML.TagSoup.Options-import Text.StringLike---parseTagsOptions :: StringLike str => ParseOptions str -> str -> [Tag str]-parseTagsOptions opts = output opts . parse . toString-
− Text/HTML/TagSoup/Match.hs
@@ -1,198 +0,0 @@--- | Combinators to match tags. Some people prefer to use @(~==)@ from--- "Text.HTML.TagSoup", others prefer these more structured combinators.--- Which you use is personal preference.------ The functions below offer maximum flexibility for matching tags.--- Using 'tagOpen', for example, you can match all links or buttons that have the "btn" class.------ For simple uses cases—like matching all comment tags, or matching opening @\<a>@ tags,--- use the tag identification functions in "Text.HTML.TagSoup#tag-identification".-module Text.HTML.TagSoup.Match where--import Text.HTML.TagSoup.Type (Tag(..), Attribute)-import Data.List (tails)---- * Matching Tags---- | Match an opening tag------ ==== __Examples__------ /Matching an opening @\<a>@ tag with a @"btn"@ class:/------ >>> let tag = TagOpen "a" [("class", "btn")]--- >>> tagOpen (== "a") (\attrs -> any (== ("class", "btn")) attrs) tag--- True-tagOpen :: (str -> Bool) -> ([Attribute str] -> Bool) -> Tag str -> Bool-tagOpen pName pAttrs (TagOpen name attrs) =- pName name && pAttrs attrs-tagOpen _ _ _ = False---- | Match a closing tag------ ==== __Examples__------ /Matching a closing @\<\/a>@ tag:/------ >>> tagClose (== "a") (TagClose "a")--- True------ >>> tagClose (== "a") (TagOpen "a" [])--- False-tagClose :: (str -> Bool) -> Tag str -> Bool-tagClose pName (TagClose name) = pName name-tagClose _ _ = False---- | Match text tags------ ==== __Examples__------ /Match all text tags:/------ >>> let tags = parseTags "<p>This is a paragraph</p>"--- [TagOpen "p" [],TagText "This is a paragraph",TagClose "p"]--- >>> filter (tagText (const True)) tags--- [TagText "This is a paragraph"]-tagText :: (str -> Bool) -> Tag str -> Bool-tagText p (TagText text) = p text-tagText _ _ = False---- | Match comment tags------ ==== __Examples__------ /Matching comment tags that include an exclamation mark:/------ >>> let tags = parseTags "<!--This is a comment-->"--- [TagComment "This is a comment!"]--- >>> all (tagComment (\s -> '!' `elem` s)) tags--- True-tagComment :: (str -> Bool) -> Tag str -> Bool-tagComment p (TagComment text) = p text-tagComment _ _ = False----- | Match an opening tag's name literally------ ==== __Examples__------ /Matching @\<a>@ tags with the @id@ "foo":/------ >>> let tag = TagOpen "a" [("id", "foo")]--- TagOpen "a" [("id","foo")]--- >>> tagOpenLit "a" (\attrs -> any (== ("id", "foo")) attrs) tag--- True----tagOpenLit :: Eq str => str -> ([Attribute str] -> Bool) -> Tag str -> Bool-tagOpenLit name = tagOpen (name==)---- | Match a closing tag's name literally------ ==== __Examples__------ /Match a closing @\<a>@ tag:/------ >>> tagCloseLit "a" (TagClose "a")--- True------ >>> tagCloseLit "a" (TagClose "em")--- False-tagCloseLit :: Eq str => str -> Tag str -> Bool-tagCloseLit name = tagClose (name==)---- | Match an opening tag's name literally, and at least one of its attributes------ ==== __Examples__------ /Matching a @\<div>@ tag with the @id@ "foo":/------ >>> tagOpenAttrLit "div" ("id", "foo") (TagOpen "div" [("id", "foo")])--- True-tagOpenAttrLit :: Eq str => str -> Attribute str -> Tag str -> Bool-tagOpenAttrLit name attr =- tagOpenLit name (anyAttrLit attr)--{- |-Match a tag with given name, that contains an attribute-with given name, that satisfies a predicate.-If an attribute occurs multiple times,-all occurrences are checked.--==== __Examples__--/Matching an @\<a>@ tag with an ID that starts with "comment-":/-->>> let commentTag = TagOpen "a" [("id", "comment-45678")]->>> tagOpenAttrNameLit "a" "id" (\idValue -> "comment-" `Data.List.isPrefixOf` idValue) commentTag-True--}-tagOpenAttrNameLit :: Eq str => str -> str -> (str -> Bool) -> Tag str -> Bool-tagOpenAttrNameLit tagName attrName pAttrValue =- tagOpenLit tagName- (anyAttr (\(name,value) -> name==attrName && pAttrValue value))----- | Check if the 'Tag str' is 'TagOpen' and matches the given name------ ==== __Examples__------ /Matching an @\<a>@ tag:/------ >>> tagOpenNameLit "a" (TagOpen "a" [])--- True------ >>> tagOpenNameLit "a" (TagOpen "div" [])--- False-tagOpenNameLit :: Eq str => str -> Tag str -> Bool-tagOpenNameLit name = tagOpenLit name (const True)---- | Check if the 'Tag str' is 'TagClose' and matches the given name------ ==== __Examples__------ /Matching a closing @\<\/a>@ tag:/------ >>> tagCloseNameLit "a" (TagClose "a")--- True------ >>> tagCloseNameLit "a" (TagClose "div")--- False-tagCloseNameLit :: Eq str => str -> Tag str -> Bool-tagCloseNameLit name = tagCloseLit name----- * Matching attributes---- | Does any attribute name/value match the predicate.-anyAttr :: ((str,str) -> Bool) -> [Attribute str] -> Bool-anyAttr = any---- | Does any attribute name match the predicate.-anyAttrName :: (str -> Bool) -> [Attribute str] -> Bool-anyAttrName p = any (p . fst)---- | Does any attribute value match the predicate.-anyAttrValue :: (str -> Bool) -> [Attribute str] -> Bool-anyAttrValue p = any (p . snd)----- | Does any attribute name/value match.-anyAttrLit :: Eq str => (str,str) -> [Attribute str] -> Bool-anyAttrLit attr = anyAttr (attr==)---- | Does any attribute name match.-anyAttrNameLit :: Eq str => str -> [Attribute str] -> Bool-anyAttrNameLit name = anyAttrName (name==)---- | Does any attribute value match.-anyAttrValueLit :: Eq str => str -> [Attribute str] -> Bool-anyAttrValueLit value = anyAttrValue (value==)------ | Get the tags under tags with a given name where the attributes match some predicate.-getTagContent :: Eq str => str -> ([Attribute str] -> Bool) -> [Tag str] -> [Tag str]-getTagContent name pAttrs =- takeWhile (not . tagCloseLit name) . drop 1 .- head . sections (tagOpenLit name pAttrs)- where sections p = filter (p . head) . init . tails
− Text/HTML/TagSoup/Options.hs
@@ -1,80 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}--module Text.HTML.TagSoup.Options where--import Data.Typeable-import Text.HTML.TagSoup.Type-import Text.HTML.TagSoup.Entity-import Text.StringLike----- | These options control how 'parseTags' works. The 'ParseOptions' type is usually generated by one of--- 'parseOptions', 'parseOptionsFast' or 'parseOptionsEntities', then selected fields may be overriden.------ The options 'optTagPosition' and 'optTagWarning' specify whether to generate--- 'TagPosition' or 'TagWarning' elements respectively. Usually these options should be set to @False@--- to simplify future stages, unless you rely on position information or want to give malformed HTML--- messages to the end user.------ The options 'optEntityData' and 'optEntityAttrib' control how entities, for example @ @ are handled.--- Both take a string, and a boolean, where @True@ indicates that the entity ended with a semi-colon @;@.--- Inside normal text 'optEntityData' will be called, and the results will be inserted in the tag stream.--- Inside a tag attribute 'optEntityAttrib' will be called, and the first component of the result will be used--- in the attribute, and the second component will be appended after the 'TagOpen' value (usually the second--- component is @[]@). As an example, to not decode any entities, pass:------ > parseOptions--- > {optEntityData=\(str,b) -> [TagText $ "&" ++ str ++ [';' | b]]--- > ,optEntityAttrib\(str,b) -> ("&" ++ str ++ [';' | b], [])---- The 'optTagTextMerge' value specifies if you always want adjacent 'TagText' values to be merged.--- Merging adjacent pieces of text has a small performance penalty, but will usually make subsequent analysis--- simpler. Contiguous runs of characters without entities or tags will also be generated as single 'TagText'--- values.-data ParseOptions str = ParseOptions- {optTagPosition :: Bool -- ^ Should 'TagPosition' values be given before some items (default=False,fast=False).- ,optTagWarning :: Bool -- ^ Should 'TagWarning' values be given (default=False,fast=False)- ,optEntityData :: (str,Bool) -> [Tag str] -- ^ How to lookup an entity (Bool = has ending @';'@)- ,optEntityAttrib :: (str,Bool) -> (str,[Tag str]) -- ^ How to lookup an entity in an attribute (Bool = has ending @';'@?)- ,optTagTextMerge :: Bool -- ^ Require no adjacent 'TagText' values (default=True,fast=False)- }- deriving Typeable----- | A 'ParseOptions' structure using a custom function to lookup attributes. Any attribute--- that is not found will be left intact, and a 'TagWarning' given (if 'optTagWarning' is set).------ If you do not want to resolve any entities, simpliy pass @const Nothing@ for the lookup function.-parseOptionsEntities :: StringLike str => (str -> Maybe str) -> ParseOptions str-parseOptionsEntities lookupEntity = ParseOptions False False entityData entityAttrib True- where- entityData x = TagText a : b- where (a,b) = entityAttrib x-- entityAttrib ~(x,b) =- let x' = x `append` fromString [';'|b]- in case lookupEntity x' of- Just y -> (y, [])- Nothing -> (fromChar '&' `append` x'- ,[TagWarning $ fromString "Unknown entity: " `append` x])----- | The default parse options value, described in 'ParseOptions'. Equivalent to--- @'parseOptionsEntities' 'lookupEntity'@.-parseOptions :: StringLike str => ParseOptions str-parseOptions = parseOptionsEntities $ fmap fromString . lookupEntity . toString----- | A 'ParseOptions' structure optimised for speed, following the fast options.-parseOptionsFast :: StringLike str => ParseOptions str-parseOptionsFast = parseOptions{optTagTextMerge=False}----- | Change the underlying string type of a 'ParseOptions' value.-fmapParseOptions :: (StringLike from, StringLike to) => ParseOptions from -> ParseOptions to-fmapParseOptions (ParseOptions a b c d e) = ParseOptions a b c2 d2 e- where- c2 ~(x,y) = map (fmap castString) $ c (castString x, y)- d2 ~(x,y) = (castString r, map (fmap castString) s)- where (r,s) = d (castString x, y)-
− Text/HTML/TagSoup/Parser.hs
@@ -1,25 +0,0 @@--module Text.HTML.TagSoup.Parser(- parseTags, parseTagsOptions,- ParseOptions(..), parseOptions, parseOptionsFast, parseOptionsEntities- ) where--import Text.HTML.TagSoup.Type-import Text.HTML.TagSoup.Options-import qualified Text.HTML.TagSoup.Generated as Gen----- | Parse a string to a list of tags, using an HTML 5 compliant parser.------ > parseTags "<hello>my&</world>" == [TagOpen "hello" [],TagText "my&",TagClose "world"]-parseTags :: StringLike str => str -> [Tag str]-parseTags = parseTagsOptions parseOptions----- | Parse a string to a list of tags, using settings supplied by the 'ParseOptions' parameter,--- eg. to output position information:------ > parseTagsOptions parseOptions{optTagPosition = True} "<hello>my&</world>" ==--- > [TagPosition 1 1,TagOpen "hello" [],TagPosition 1 8,TagText "my&",TagPosition 1 15,TagClose "world"]-parseTagsOptions :: StringLike str => ParseOptions str -> str -> [Tag str]-parseTagsOptions = Gen.parseTagsOptions
− Text/HTML/TagSoup/Render.hs
@@ -1,80 +0,0 @@-{-# LANGUAGE PatternGuards, OverloadedStrings #-}-{-|- This module converts a list of 'Tag' back into a string.--}--module Text.HTML.TagSoup.Render- (- renderTags, renderTagsOptions, escapeHTML,- RenderOptions(..), renderOptions- ) where--import Text.HTML.TagSoup.Entity-import Text.HTML.TagSoup.Type-import Text.StringLike----- | These options control how 'renderTags' works.------ The strange quirk of only minimizing @\<br\>@ tags is due to Internet Explorer treating--- @\<br\>\<\/br\>@ as @\<br\>\<br\>@.-data RenderOptions str = RenderOptions- {optEscape :: str -> str -- ^ Escape a piece of text (default = escape the four characters @&\"\<\>@)- ,optMinimize :: str -> Bool -- ^ Minimise \<b\>\<\/b\> -> \<b/\> (default = minimise only @\<br\>@ tags)- ,optRawTag :: str -> Bool -- ^ Should a tag be output with no escaping (default = true only for @script@)- }----- | Replace the four characters @&\"\<\>@ with their HTML entities ('escapeXML' lifted to 'StringLike').-escapeHTML :: StringLike str => str -> str-escapeHTML = fromString . escapeXML . toString---- | The default render options value, described in 'RenderOptions'.-renderOptions :: StringLike str => RenderOptions str-renderOptions = RenderOptions escapeHTML (\x -> toString x == "br") (\x -> toString x == "script")----- | Show a list of tags, as they might have been parsed, using the default settings given in--- 'RenderOptions'.------ > renderTags [TagOpen "hello" [],TagText "my&",TagClose "world"] == "<hello>my&</world>"-renderTags :: StringLike str => [Tag str] -> str-renderTags = renderTagsOptions renderOptions----- | Show a list of tags using settings supplied by the 'RenderOptions' parameter,--- eg. to avoid escaping any characters one could do:------ > renderTagsOptions renderOptions{optEscape = id} [TagText "my&"] == "my&"-renderTagsOptions :: StringLike str => RenderOptions str -> [Tag str] -> str-renderTagsOptions opts = strConcat . tags- where- ss x = [x]-- tags (TagOpen name atts:TagClose name2:xs)- | name == name2 && optMinimize opts name = open name atts " /" ++ tags xs- tags (TagOpen name atts:xs)- | Just ('?',_) <- uncons name = open name atts " ?" ++ tags xs- | optRawTag opts name =- let (a,b) = break (== TagClose name) (TagOpen name atts:xs)- in concatMap (\x -> case x of TagText s -> [s]; _ -> tag x) a ++ tags b- tags (x:xs) = tag x ++ tags xs- tags [] = []-- tag (TagOpen name atts) = open name atts ""- tag (TagClose name) = ["</", name, ">"]- tag (TagText text) = [txt text]- tag (TagComment text) = ss "<!--" ++ com text ++ ss "-->"- tag _ = ss ""-- txt = optEscape opts- open name atts shut = ["<",name] ++ concatMap att atts ++ [shut,">"]- att ("","") = [" \"\""]- att (x ,"") = [" ", x]- att ("", y) = [" \"",txt y,"\""]- att (x , y) = [" ",x,"=\"",txt y,"\""]-- com xs | Just ('-',xs) <- uncons xs, Just ('-',xs) <- uncons xs, Just ('>',xs) <- uncons xs = "-- >" : com xs- com xs = case uncons xs of- Nothing -> []- Just (x,xs) -> fromChar x : com xs
− Text/HTML/TagSoup/Specification.hs
@@ -1,332 +0,0 @@-{-# LANGUAGE RecordWildCards, PatternGuards #-}--module Text.HTML.TagSoup.Specification(parse) where--import Text.HTML.TagSoup.Implementation-import Data.Char (isAlpha, isAlphaNum, isDigit, toLower)---- We make some generalisations:--- <!name is a valid tag start closed by >--- <?name is a valid tag start closed by ?>--- </!name> is a valid closing tag--- </?name> is a valid closing tag--- <a "foo"> is a valid tag attibute in ! and ?, i.e missing an attribute name--- We also don't do lowercase conversion--- Entities are handled without a list of known entity names--- We don't have RCData, CData or Escape modes (only effects dat and tagOpen)---data TypeTag = TypeNormal -- <foo- | TypeXml -- <?foo- | TypeDecl -- <!foo- | TypeScript -- <script- deriving Eq----- 2.4.1 Common parser idioms-white x = x `elem` " \t\n\f\r"----- 8.2.4 Tokenization--type Parser = S -> [Out]--parse :: String -> [Out]-parse = dat . state ---- 8.2.4.1 Data state-dat :: Parser-dat S{..} = pos $ case hd of- '&' -> charReference tl- '<' -> tagOpen tl- _ | eof -> []- _ -> hd & dat tl----- 8.2.4.2 Character reference data state-charReference s = charRef dat False Nothing s----- 8.2.4.3 Tag open state-tagOpen S{..} = case hd of- '!' -> markupDeclOpen tl- '/' -> closeTagOpen tl- _ | isAlpha hd -> Tag & hd & tagName (if isScript s then TypeScript else TypeNormal) tl- '>' -> errSeen "<>" & '<' & '>' & dat tl- '?' -> neilXmlTagOpen tl -- NEIL- _ -> errSeen "<" & '<' & dat s--isScript = f "script"- where- f (c:cs) S{..} = toLower hd == c && f cs tl- f [] S{..} = white hd || hd == '/' || hd == '>' || hd == '?' || eof----- seen "<?", emitted []-neilXmlTagOpen S{..} = pos $ case hd of- _ | isAlpha hd -> Tag & '?' & hd & tagName TypeXml tl- _ -> errSeen "<?" & '<' & '?' & dat s---- seen "?", expecting ">"-neilXmlTagClose S{..} = pos $ case hd of- '>' -> TagEnd & dat tl- _ -> errSeen "?" & beforeAttName TypeXml s----- just seen ">" at the end, am given tl-neilTagEnd typ S{..}- | typ == TypeXml = pos $ errWant "?>" & TagEnd & dat s- | typ == TypeScript = pos $ TagEnd & neilScriptBody s- | otherwise = pos $ TagEnd & dat s---- Inside a <script> tag, only break on </script-neilScriptBody o@S{..}- | hd == '<', S{..} <- tl- , hd == '/', S{..} <- tl- , isScript s- = dat o- | eof = []- | otherwise = pos $ hd & neilScriptBody tl----- 8.2.4.4 Close tag open state--- Deviation: We ignore the if CDATA/RCDATA bits and tag matching--- Deviation: On </> we output </> to the text--- Deviation: </!name> is a closing tag, not a bogus comment-closeTagOpen S{..} = case hd of- _ | isAlpha hd || hd `elem` "?!" -> TagShut & hd & tagName TypeNormal tl- '>' -> errSeen "</>" & '<' & '/' & '>' & dat tl- _ | eof -> '<' & '/' & dat s- _ -> errWant "tag name" & bogusComment s----- 8.2.4.5 Tag name state-tagName typ S{..} = pos $ case hd of- _ | white hd -> beforeAttName typ tl- '/' -> selfClosingStartTag typ tl- '>' -> neilTagEnd typ tl- '?' | typ == TypeXml -> neilXmlTagClose tl- _ | isAlpha hd -> hd & tagName typ tl- _ | eof -> errWant (if typ == TypeXml then "?>" else ">") & dat s- _ -> hd & tagName typ tl----- 8.2.4.6 Before attribute name state-beforeAttName typ S{..} = pos $ case hd of- _ | white hd -> beforeAttName typ tl- '/' -> selfClosingStartTag typ tl- '>' -> neilTagEnd typ tl- '?' | typ == TypeXml -> neilXmlTagClose tl- _ | typ /= TypeNormal && hd `elem` "\'\"" -> beforeAttValue typ s -- NEIL- _ | hd `elem` "\"'<=" -> errSeen [hd] & AttName & hd & attName typ tl- _ | eof -> errWant (if typ == TypeXml then "?>" else ">") & dat s- _ -> AttName & hd & attName typ tl----- 8.2.4.7 Attribute name state-attName typ S{..} = pos $ case hd of- _ | white hd -> afterAttName typ tl- '/' -> selfClosingStartTag typ tl- '=' -> beforeAttValue typ tl- '>' -> neilTagEnd typ tl- '?' | typ == TypeXml -> neilXmlTagClose tl- _ | hd `elem` "\"'<" -> errSeen [hd] & def- _ | eof -> errWant (if typ == TypeXml then "?>" else ">") & dat s- _ -> def- where def = hd & attName typ tl----- 8.2.4.8 After attribute name state-afterAttName typ S{..} = pos $ case hd of- _ | white hd -> afterAttName typ tl- '/' -> selfClosingStartTag typ tl- '=' -> beforeAttValue typ tl- '>' -> neilTagEnd typ tl- '?' | typ == TypeXml -> neilXmlTagClose tl- _ | typ /= TypeNormal && hd `elem` "\"'" -> AttVal & beforeAttValue typ s -- NEIL- _ | hd `elem` "\"'<" -> errSeen [hd] & def- _ | eof -> errWant (if typ == TypeXml then "?>" else ">") & dat s- _ -> def- where def = AttName & hd & attName typ tl---- 8.2.4.9 Before attribute value state-beforeAttValue typ S{..} = pos $ case hd of- _ | white hd -> beforeAttValue typ tl- '\"' -> AttVal & attValueDQuoted typ tl- '&' -> AttVal & attValueUnquoted typ s- '\'' -> AttVal & attValueSQuoted typ tl- '>' -> errSeen "=" & neilTagEnd typ tl- '?' | typ == TypeXml -> neilXmlTagClose tl- _ | hd `elem` "<=" -> errSeen [hd] & def- _ | eof -> errWant (if typ == TypeXml then "?>" else ">") & dat s- _ -> def- where def = AttVal & hd & attValueUnquoted typ tl----- 8.2.4.10 Attribute value (double-quoted) state-attValueDQuoted typ S{..} = pos $ case hd of- '\"' -> afterAttValueQuoted typ tl- '&' -> charRefAttValue (attValueDQuoted typ) (Just '\"') tl- _ | eof -> errWant "\"" & dat s- _ -> hd & attValueDQuoted typ tl----- 8.2.4.11 Attribute value (single-quoted) state-attValueSQuoted typ S{..} = pos $ case hd of- '\'' -> afterAttValueQuoted typ tl- '&' -> charRefAttValue (attValueSQuoted typ) (Just '\'') tl- _ | eof -> errWant "\'" & dat s- _ -> hd & attValueSQuoted typ tl----- 8.2.4.12 Attribute value (unquoted) state-attValueUnquoted typ S{..} = pos $ case hd of- _ | white hd -> beforeAttName typ tl- '&' -> charRefAttValue (attValueUnquoted typ) Nothing tl- '>' -> neilTagEnd typ tl- '?' | typ == TypeXml -> neilXmlTagClose tl- _ | hd `elem` "\"'<=" -> errSeen [hd] & def- _ | eof -> errWant (if typ == TypeXml then "?>" else ">") & dat s- _ -> def- where def = hd & attValueUnquoted typ tl----- 8.2.4.13 Character reference in attribute value state-charRefAttValue :: Parser -> Maybe Char -> Parser-charRefAttValue resume c s = charRef resume True c s----- 8.2.4.14 After attribute value (quoted) state-afterAttValueQuoted typ S{..} = pos $ case hd of- _ | white hd -> beforeAttName typ tl- '/' -> selfClosingStartTag typ tl- '>' -> neilTagEnd typ tl- '?' | typ == TypeXml -> neilXmlTagClose tl- _ | eof -> dat s- _ -> errSeen [hd] & beforeAttName typ s----- 8.2.4.15 Self-closing start tag state-selfClosingStartTag typ S{..} = pos $ case hd of- _ | typ == TypeXml -> errSeen "/" & beforeAttName typ s- '>' -> TagEndClose & dat tl- _ | eof -> errWant ">" & dat s- _ -> errSeen "/" & beforeAttName typ s----- 8.2.4.16 Bogus comment state-bogusComment S{..} = Comment & bogusComment1 s-bogusComment1 S{..} = pos $ case hd of- '>' -> CommentEnd & dat tl- _ | eof -> CommentEnd & dat s- _ -> hd & bogusComment1 tl----- 8.2.4.17 Markup declaration open state-markupDeclOpen S{..} = pos $ case hd of- _ | Just s <- next "--" -> Comment & commentStart s- _ | isAlpha hd -> Tag & '!' & hd & tagName TypeDecl tl -- NEIL- _ | Just s <- next "[CDATA[" -> cdataSection s- _ -> errWant "tag name" & bogusComment s----- 8.2.4.18 Comment start state-commentStart S{..} = pos $ case hd of- '-' -> commentStartDash tl- '>' -> errSeen "<!-->" & CommentEnd & dat tl- _ | eof -> errWant "-->" & CommentEnd & dat s- _ -> hd & comment tl----- 8.2.4.19 Comment start dash state-commentStartDash S{..} = pos $ case hd of- '-' -> commentEnd tl- '>' -> errSeen "<!--->" & CommentEnd & dat tl- _ | eof -> errWant "-->" & CommentEnd & dat s- _ -> '-' & hd & comment tl----- 8.2.4.20 Comment state-comment S{..} = pos $ case hd of- '-' -> commentEndDash tl- _ | eof -> errWant "-->" & CommentEnd & dat s- _ -> hd & comment tl----- 8.2.4.21 Comment end dash state-commentEndDash S{..} = pos $ case hd of- '-' -> commentEnd tl- _ | eof -> errWant "-->" & CommentEnd & dat s- _ -> '-' & hd & comment tl----- 8.2.4.22 Comment end state-commentEnd S{..} = pos $ case hd of- '>' -> CommentEnd & dat tl- '-' -> errWant "-->" & '-' & commentEnd tl- _ | white hd -> errSeen "--" & '-' & '-' & hd & commentEndSpace tl- '!' -> errSeen "!" & commentEndBang tl- _ | eof -> errWant "-->" & CommentEnd & dat s- _ -> errSeen "--" & '-' & '-' & hd & comment tl----- 8.2.4.23 Comment end bang state-commentEndBang S{..} = pos $ case hd of- '>' -> CommentEnd & dat tl- '-' -> '-' & '-' & '!' & commentEndDash tl- _ | eof -> errWant "-->" & CommentEnd & dat s- _ -> '-' & '-' & '!' & hd & comment tl----- 8.2.4.24 Comment end space state-commentEndSpace S{..} = pos $ case hd of- '>' -> CommentEnd & dat tl- '-' -> commentEndDash tl- _ | white hd -> hd & commentEndSpace tl- _ | eof -> errWant "-->" & CommentEnd & dat s- _ -> hd & comment tl----- 8.2.4.38 CDATA section state-cdataSection S{..} = pos $ case hd of- _ | Just s <- next "]]>" -> dat s- _ | eof -> dat s- _ | otherwise -> hd & cdataSection tl----- 8.2.4.39 Tokenizing character references--- Change from spec: this is reponsible for writing '&' if nothing is to be written-charRef :: Parser -> Bool -> Maybe Char -> S -> [Out]-charRef resume att end S{..} = pos $ case hd of- _ | eof || hd `elem` "\t\n\f <&" || maybe False (== hd) end -> '&' & resume s- '#' -> charRefNum resume s tl- _ -> charRefAlpha resume att s--charRefNum resume o S{..} = pos $ case hd of- _ | hd `elem` "xX" -> charRefNum2 resume o True tl- _ -> charRefNum2 resume o False s--charRefNum2 resume o hex S{..} = pos $ case hd of- _ | hexChar hex hd -> (if hex then EntityHex else EntityNum) & hd & charRefNum3 resume hex tl- _ -> errSeen "&" & '&' & resume o--charRefNum3 resume hex S{..} = pos $ case hd of- _ | hexChar hex hd -> hd & charRefNum3 resume hex tl- ';' -> EntityEnd True & resume tl- _ -> EntityEnd False & errWant ";" & resume s--charRefAlpha resume att S{..} = pos $ case hd of- _ | isAlpha hd -> EntityName & hd & charRefAlpha2 resume att tl- _ -> errSeen "&" & '&' & resume s--charRefAlpha2 resume att S{..} = pos $ case hd of- _ | alphaChar hd -> hd & charRefAlpha2 resume att tl- ';' -> EntityEnd True & resume tl- _ | att -> EntityEnd False & resume s- _ -> EntityEnd False & errWant ";" & resume s---alphaChar x = isAlphaNum x || x `elem` ":-_"--hexChar False x = isDigit x-hexChar True x = isDigit x || (x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F')
− Text/HTML/TagSoup/Tree.hs
@@ -1,118 +0,0 @@-{-|- /NOTE/: This module is preliminary and may change at a future date.-- This module is intended to help converting a list of tags into a- tree of tags.--}--module Text.HTML.TagSoup.Tree- (- TagTree(..), tagTree, parseTree, parseTreeOptions, ParseOptions(..),- flattenTree, renderTree, renderTreeOptions, RenderOptions(..), transformTree, universeTree- ) where--import Text.HTML.TagSoup (parseTags, parseTagsOptions, renderTags, renderTagsOptions, ParseOptions(..), RenderOptions(..))-import Text.HTML.TagSoup.Type-import Control.Arrow-import GHC.Exts (build)----- | A tree of 'Tag' values.-data TagTree str- = -- | A 'TagOpen'/'TagClose' pair with the 'Tag' values in between.- TagBranch str [Attribute str] [TagTree str]- | -- | Any leaf node- TagLeaf (Tag str)- deriving (Eq,Ord,Show)--instance Functor TagTree where- fmap f (TagBranch x y z) = TagBranch (f x) (map (f***f) y) (map (fmap f) z)- fmap f (TagLeaf x) = TagLeaf (fmap f x)----- | Convert a list of tags into a tree. This version is not lazy at--- all, that is saved for version 2.-tagTree :: Eq str => [Tag str] -> [TagTree str]-tagTree = g- where- g :: Eq str => [Tag str] -> [TagTree str]- g [] = []- g xs = a ++ map TagLeaf (take 1 b) ++ g (drop 1 b)- where (a,b) = f xs-- -- the second tuple is either null or starts with a close- f :: Eq str => [Tag str] -> ([TagTree str],[Tag str])- f (TagOpen name atts:rest) =- case f rest of- (inner,[]) -> (TagLeaf (TagOpen name atts):inner, [])- (inner,TagClose x:xs)- | x == name -> let (a,b) = f xs in (TagBranch name atts inner:a, b)- | otherwise -> (TagLeaf (TagOpen name atts):inner, TagClose x:xs)- _ -> error "TagSoup.Tree.tagTree: safe as - forall x . isTagClose (snd (f x))"-- f (TagClose x:xs) = ([], TagClose x:xs)- f (x:xs) = (TagLeaf x:a,b)- where (a,b) = f xs- f [] = ([], [])---- | Build a 'TagTree' from a string.-parseTree :: StringLike str => str -> [TagTree str]-parseTree = tagTree . parseTags---- | Build a 'TagTree' from a string, specifying the 'ParseOptions'.-parseTreeOptions :: StringLike str => ParseOptions str -> str -> [TagTree str]-parseTreeOptions opts str = tagTree $ parseTagsOptions opts str---- | Flatten a 'TagTree' back to a list of 'Tag'.-flattenTree :: [TagTree str] -> [Tag str]-flattenTree xs = build $ flattenTreeFB xs--flattenTreeFB :: [TagTree str] -> (Tag str -> lst -> lst) -> lst -> lst-flattenTreeFB xs cons nil = flattenTreeOnto xs nil- where- flattenTreeOnto [] tags = tags- flattenTreeOnto (TagBranch name atts inner:trs) tags =- TagOpen name atts `cons` flattenTreeOnto inner (TagClose name `cons` flattenTreeOnto trs tags)- flattenTreeOnto (TagLeaf x:trs) tags = x `cons` flattenTreeOnto trs tags---- | Render a 'TagTree'.-renderTree :: StringLike str => [TagTree str] -> str-renderTree = renderTags . flattenTree---- | Render a 'TagTree' with some 'RenderOptions'.-renderTreeOptions :: StringLike str => RenderOptions str -> [TagTree str] -> str-renderTreeOptions opts trees = renderTagsOptions opts $ flattenTree trees---- | This operation is based on the Uniplate @universe@ function. Given a--- list of trees, it returns those trees, and all the children trees at--- any level. For example:------ > universeTree--- > [TagBranch "a" [("href","url")] [TagBranch "b" [] [TagLeaf (TagText "text")]]]--- > == [TagBranch "a" [("href","url")] [TagBranch "b" [] [TagLeaf (TagText "text")]]]--- > ,TagBranch "b" [] [TagLeaf (TagText "text")]]------ This operation is particularly useful for queries. To collect all @\"a\"@--- tags in a tree, simply do:------ > [x | x@(TagBranch "a" _ _) <- universeTree tree]-universeTree :: [TagTree str] -> [TagTree str]-universeTree = concatMap f- where- f t@(TagBranch _ _ inner) = t : universeTree inner- f x = [x]----- | This operation is based on the Uniplate @transform@ function. Given a--- list of trees, it applies the function to every tree in a bottom-up--- manner. This operation is useful for manipulating a tree - for example--- to make all tag names upper case:------ > upperCase = transformTree f--- > where f (TagBranch name atts inner) = [TagBranch (map toUpper name) atts inner]--- > f x = [x]-transformTree :: (TagTree str -> [TagTree str]) -> [TagTree str] -> [TagTree str]-transformTree act = concatMap f- where- f (TagBranch a b inner) = act $ TagBranch a b (transformTree act inner)- f x = act x
− Text/HTML/TagSoup/Type.hs
@@ -1,142 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}--- | The central type in TagSoup--module Text.HTML.TagSoup.Type(- -- * Data structures and parsing- StringLike, Tag(..), Attribute, Row, Column,- - -- * Position manipulation- Position(..), tagPosition, nullPosition, positionChar, positionString,-- -- * Tag identification- isTagOpen, isTagClose, isTagText, isTagWarning, isTagPosition,- isTagOpenName, isTagCloseName, isTagComment,-- -- * Extraction- fromTagText, fromAttrib,- maybeTagText, maybeTagWarning,- innerText,- ) where---import Data.List (foldl')-import Data.Maybe (fromMaybe, mapMaybe)-import Text.StringLike-import Data.Data(Data, Typeable)---- | An HTML attribute @id=\"name\"@ generates @(\"id\",\"name\")@-type Attribute str = (str,str)---- | The row/line of a position, starting at 1-type Row = Int---- | The column of a position, starting at 1-type Column = Int------ All positions are stored as a row and a column, with (1,1) being the---- top-left position-data Position = Position !Row !Column deriving (Show,Eq,Ord)--nullPosition :: Position-nullPosition = Position 1 1--positionString :: Position -> String -> Position-positionString = foldl' positionChar--positionChar :: Position -> Char -> Position-positionChar (Position r c) x = case x of- '\n' -> Position (r+1) 1- '\t' -> Position r (c + 8 - mod (c-1) 8)- _ -> Position r (c+1)--tagPosition :: Position -> Tag str-tagPosition (Position r c) = TagPosition r c----- | A single HTML element. A whole document is represented by a list of @Tag@.--- There is no requirement for 'TagOpen' and 'TagClose' to match.-data Tag str =- TagOpen str [Attribute str] -- ^ An open tag with 'Attribute's in their original order- | TagClose str -- ^ A closing tag- | TagText str -- ^ A text node, guaranteed not to be the empty string- | TagComment str -- ^ A comment- | TagWarning str -- ^ Meta: A syntax error in the input file- | TagPosition !Row !Column -- ^ Meta: The position of a parsed element- deriving (Show, Eq, Ord, Data, Typeable)--instance Functor Tag where- fmap f (TagOpen x y) = TagOpen (f x) [(f a, f b) | (a,b) <- y]- fmap f (TagClose x) = TagClose (f x)- fmap f (TagText x) = TagText (f x)- fmap f (TagComment x) = TagComment (f x)- fmap f (TagWarning x) = TagWarning (f x)- fmap f (TagPosition x y) = TagPosition x y----- | Test if a 'Tag' is a 'TagOpen'-isTagOpen :: Tag str -> Bool-isTagOpen (TagOpen {}) = True; isTagOpen _ = False---- | Test if a 'Tag' is a 'TagClose'-isTagClose :: Tag str -> Bool-isTagClose (TagClose {}) = True; isTagClose _ = False---- | Test if a 'Tag' is a 'TagText'-isTagText :: Tag str -> Bool-isTagText (TagText {}) = True; isTagText _ = False---- | Extract the string from within 'TagText', otherwise 'Nothing'-maybeTagText :: Tag str -> Maybe str-maybeTagText (TagText x) = Just x-maybeTagText _ = Nothing---- | Extract the string from within 'TagText', crashes if not a 'TagText'-fromTagText :: Show str => Tag str -> str-fromTagText (TagText x) = x-fromTagText x = error $ "(" ++ show x ++ ") is not a TagText"---- | Extract all text content from tags (similar to Verbatim found in HaXml)-innerText :: StringLike str => [Tag str] -> str-innerText = strConcat . mapMaybe maybeTagText---- | Test if a 'Tag' is a 'TagWarning'-isTagWarning :: Tag str -> Bool-isTagWarning (TagWarning {}) = True; isTagWarning _ = False---- | Extract the string from within 'TagWarning', otherwise 'Nothing'-maybeTagWarning :: Tag str -> Maybe str-maybeTagWarning (TagWarning x) = Just x-maybeTagWarning _ = Nothing---- | Test if a 'Tag' is a 'TagPosition'-isTagPosition :: Tag str -> Bool-isTagPosition TagPosition{} = True; isTagPosition _ = False---- | Extract an attribute, crashes if not a 'TagOpen'.--- Returns @\"\"@ if no attribute present.------ Warning: does not distinquish between missing attribute--- and present attribute with value @\"\"@.-fromAttrib :: (Show str, Eq str, StringLike str) => str -> Tag str -> str-fromAttrib att tag = fromMaybe empty $ maybeAttrib att tag---- | Extract an attribute, crashes if not a 'TagOpen'.--- Returns @Nothing@ if no attribute present.-maybeAttrib :: (Show str, Eq str) => str -> Tag str -> Maybe str-maybeAttrib att (TagOpen _ atts) = lookup att atts-maybeAttrib _ x = error ("(" ++ show x ++ ") is not a TagOpen")---- | Returns True if the 'Tag' is 'TagOpen' and matches the given name-isTagOpenName :: Eq str => str -> Tag str -> Bool-isTagOpenName name (TagOpen n _) = n == name-isTagOpenName _ _ = False---- | Returns True if the 'Tag' is 'TagClose' and matches the given name-isTagCloseName :: Eq str => str -> Tag str -> Bool-isTagCloseName name (TagClose n) = n == name-isTagCloseName _ _ = False---- | Test if a 'Tag' is a 'TagComment'-isTagComment :: Tag str -> Bool-isTagComment TagComment {} = True; isTagComment _ = False
− Text/StringLike.hs
@@ -1,96 +0,0 @@-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}---- | /WARNING/: This module is /not/ intended for use outside the TagSoup library.------ This module provides an abstraction for String's as used inside TagSoup. It allows--- TagSoup to work with String (list of Char), ByteString.Char8, ByteString.Lazy.Char8,--- Data.Text and Data.Text.Lazy.-module Text.StringLike (StringLike(..), fromString, castString) where--import Data.String-import Data.Typeable--import qualified Data.ByteString.Char8 as BS-import qualified Data.ByteString.Lazy.Char8 as LBS-import qualified Data.Text as T-import qualified Data.Text.Lazy as LT----- | A class to generalise TagSoup parsing over many types of string-like types.--- Examples are given for the String type.-class (Typeable a, Eq a, IsString a) => StringLike a where- -- | > empty = ""- empty :: a- -- | > cons = (:)- cons :: Char -> a -> a- -- | > uncons [] = Nothing- -- > uncons (x:xs) = Just (x, xs)- uncons :: a -> Maybe (Char, a)-- -- | > toString = id- toString :: a -> String- -- | > fromChar = return- fromChar :: Char -> a- -- | > strConcat = concat- strConcat :: [a] -> a- -- | > strNull = null- strNull :: a -> Bool- -- | > append = (++)- append :: a -> a -> a----- | Convert a String from one type to another.-castString :: (StringLike a, StringLike b) => a -> b-castString = fromString . toString---instance StringLike String where- uncons [] = Nothing- uncons (x:xs) = Just (x, xs)- toString = id- fromChar = (:[])- strConcat = concat- empty = []- strNull = null- cons c = (c:)- append = (++)--instance StringLike BS.ByteString where- uncons = BS.uncons- toString = BS.unpack- fromChar = BS.singleton- strConcat = BS.concat- empty = BS.empty- strNull = BS.null- cons = BS.cons- append = BS.append--instance StringLike LBS.ByteString where- uncons = LBS.uncons- toString = LBS.unpack- fromChar = LBS.singleton- strConcat = LBS.concat- empty = LBS.empty- strNull = LBS.null- cons = LBS.cons- append = LBS.append--instance StringLike T.Text where- uncons = T.uncons- toString = T.unpack- fromChar = T.singleton- strConcat = T.concat- empty = T.empty- strNull = T.null- cons = T.cons- append = T.append--instance StringLike LT.Text where- uncons = LT.uncons- toString = LT.unpack- fromChar = LT.singleton- strConcat = LT.concat- empty = LT.empty- strNull = LT.null- cons = LT.cons- append = LT.append
+ src/Text/HTML/TagSoup.hs view
@@ -0,0 +1,117 @@+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, PatternGuards #-}++{-|+ This module is for working with HTML/XML. It deals with both well-formed XML and+ malformed HTML from the web. It features:++ * A lazy parser, based on the HTML 5 specification - see 'parseTags'.++ * A renderer that can write out HTML/XML - see 'renderTags'.++ * Utilities for extracting information from a document - see '~==', 'sections' and 'partitions'.++ The standard practice is to parse a 'String' to @[@'Tag' 'String'@]@ using 'parseTags',+ then operate upon it to extract the necessary information.+-}++module Text.HTML.TagSoup(+ -- * Data structures and parsing+ Tag(..), Row, Column, Attribute,+ module Text.HTML.TagSoup.Parser,+ module Text.HTML.TagSoup.Render,+ canonicalizeTags,++ -- Note: the "#tag-identification#" creates an anchor that's linked to from Match.hs+ -- * #tag-identification# Tag identification+ isTagOpen, isTagClose, isTagText, isTagWarning, isTagPosition,+ isTagOpenName, isTagCloseName, isTagComment,++ -- * Extraction+ fromTagText, fromAttrib,+ maybeTagText, maybeTagWarning,+ innerText,++ -- * Utility+ sections, partitions,+ + -- * Combinators+ TagRep(..), (~==),(~/=)+ ) where++import Text.HTML.TagSoup.Type+import Text.HTML.TagSoup.Parser+import Text.HTML.TagSoup.Render+import Data.Char+import Data.List (groupBy, tails)+import Text.StringLike+++-- | Turns all tag names and attributes to lower case and+-- converts DOCTYPE to upper case.+canonicalizeTags :: StringLike str => [Tag str] -> [Tag str]+canonicalizeTags = map f+ where+ f (TagOpen tag attrs) | Just ('!',name) <- uncons tag = TagOpen ('!' `cons` ucase name) attrs+ f (TagOpen name attrs) = TagOpen (lcase name) [(lcase k, v) | (k,v) <- attrs]+ f (TagClose name) = TagClose (lcase name)+ f a = a++ ucase = fromString . map toUpper . toString+ lcase = fromString . map toLower . toString+++-- | Define a class to allow String's or Tag str's to be used as matches+class TagRep a where+ -- | Convert a value into a 'Tag'.+ toTagRep :: StringLike str => a -> Tag str++instance StringLike str => TagRep (Tag str) where toTagRep = fmap castString++instance TagRep String where+ toTagRep x = case parseTags x of+ [a] -> toTagRep a+ _ -> error $ "When using a TagRep it must be exactly one tag, you gave: " ++ x++++-- | Performs an inexact match, the first item should be the thing to match.+-- If the second item is a blank string, that is considered to match anything.+-- For example:+--+-- > (TagText "test" ~== TagText "" ) == True+-- > (TagText "test" ~== TagText "test") == True+-- > (TagText "test" ~== TagText "soup") == False+--+-- For 'TagOpen' missing attributes on the right are allowed.+(~==) :: (StringLike str, TagRep t) => Tag str -> t -> Bool+(~==) a b = f a (toTagRep b)+ where+ f (TagText y) (TagText x) = strNull x || x == y+ f (TagClose y) (TagClose x) = strNull x || x == y+ f (TagOpen y ys) (TagOpen x xs) = (strNull x || x == y) && all g xs+ where+ g (name,val) | strNull name = val `elem` map snd ys+ | strNull val = name `elem` map fst ys+ g nameval = nameval `elem` ys+ f (TagComment x) (TagComment y) = strNull x || x == y+ f (TagWarning x) (TagWarning y) = strNull x || x == y+ f (TagPosition x1 x2) (TagPosition y1 y2) = x1 == y1 && x2 == y2+ f _ _ = False++-- | Negation of '~=='+(~/=) :: (StringLike str, TagRep t) => Tag str -> t -> Bool+(~/=) a b = not (a ~== b)++++-- | This function takes a list, and returns all suffixes whose+-- first item matches the predicate.+sections :: (a -> Bool) -> [a] -> [[a]]+sections p = filter (p . head) . init . tails++-- | This function is similar to 'sections', but splits the list+-- so no element appears in any two partitions.+partitions :: (a -> Bool) -> [a] -> [[a]]+partitions p =+ let notp = not . p+ in groupBy (const notp) . dropWhile notp
+ src/Text/HTML/TagSoup/Entity.hs view
@@ -0,0 +1,2320 @@+-- | This module converts between HTML/XML entities (i.e. @&@) and+-- the characters they represent.+module Text.HTML.TagSoup.Entity(+ lookupEntity, lookupNamedEntity, lookupNumericEntity,+ escapeXML,+ xmlEntities, htmlEntities+ ) where++import Data.Char (chr, ord)+import qualified Data.IntMap as IntMap+import Data.Ix+import qualified Data.Map as Map+import Numeric (readHex)+++-- | Lookup an entity, using 'lookupNumericEntity' if it starts with+-- @#@ and 'lookupNamedEntity' otherwise+lookupEntity :: String -> Maybe String+lookupEntity ('#':xs) = lookupNumericEntity xs+lookupEntity xs = lookupNamedEntity xs+++-- | Lookup a numeric entity, the leading @\'#\'@ must have already been removed.+--+-- > lookupNumericEntity "65" == Just "A"+-- > lookupNumericEntity "x41" == Just "A"+-- > lookupNumericEntity "x4E" === Just "N"+-- > lookupNumericEntity "x4e" === Just "N"+-- > lookupNumericEntity "X4e" === Just "N"+-- > lookupNumericEntity "Haskell" == Nothing+-- > lookupNumericEntity "" == Nothing+-- > lookupNumericEntity "89439085908539082" == Nothing+lookupNumericEntity :: String -> Maybe String+lookupNumericEntity = f+ -- entity = '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'+ where+ f (x:xs) | x `elem` "xX" = g [('0','9'),('a','f'),('A','F')] readHex xs+ f xs = g [('0','9')] reads xs++ g :: [(Char,Char)] -> ReadS Integer -> String -> Maybe String+ g valid reader xs = do+ let test b = if b then Just () else Nothing+ test $ isValid valid xs+ test $ not $ null xs+ case reader xs of+ [(a,"")] -> do+ test $ inRange (toInteger $ ord minBound, toInteger $ ord maxBound) a+ return [chr $ fromInteger a]+ _ -> Nothing++ isValid :: [(Char,Char)] -> String -> Bool+ isValid valid xs = all (\x -> any (`inRange` x) valid) xs+++-- | Lookup a named entity, using 'htmlEntities'+--+-- > lookupNamedEntity "amp" == Just "&"+-- > lookupNamedEntity "haskell" == Nothing+lookupNamedEntity :: String -> Maybe String+lookupNamedEntity = \x -> Map.lookup x mp+ where mp = Map.fromList htmlEntities+++-- | Escape an XML string.+--+-- > escapeXML "hello world" == "hello world"+-- > escapeXML "hello & world" == "hello & world"+escapeXML :: String -> String+escapeXML = concatMap $ \x -> IntMap.findWithDefault [x] (ord x) mp+ where mp = IntMap.fromList [(ord b, "&"++a++";") | (a,[b]) <- xmlEntities]+++-- | A table mapping XML entity names to resolved strings. All strings are a single character long.+-- Does /not/ include @apos@ as Internet Explorer does not know about it.+xmlEntities :: [(String, String)]+xmlEntities = let a*b = (a,[b]) in+ ["quot" * '"'+ ,"amp" * '&'+ -- ,"apos" * '\'' -- Internet Explorer does not know that+ ,"lt" * '<'+ ,"gt" * '>'+ ]++-- | A table mapping HTML entity names to resolved strings. Most resolved strings are a single character long,+-- but some (e.g. @"ngeqq"@) are two characters long. The list is taken from+-- <http://www.w3.org/TR/html5/syntax.html#named-character-references>.+htmlEntities :: [(String, String)]+htmlEntities = let (*) = (,) in+ ["Aacute" * "\x00C1"+ ,"aacute" * "\x00E1"+ ,"Aacute;" * "\x00C1"+ ,"aacute;" * "\x00E1"+ ,"Abreve;" * "\x0102"+ ,"abreve;" * "\x0103"+ ,"ac;" * "\x223E"+ ,"acd;" * "\x223F"+ ,"acE;" * "\x223E\x0333"+ ,"Acirc" * "\x00C2"+ ,"acirc" * "\x00E2"+ ,"Acirc;" * "\x00C2"+ ,"acirc;" * "\x00E2"+ ,"acute" * "\x00B4"+ ,"acute;" * "\x00B4"+ ,"Acy;" * "\x0410"+ ,"acy;" * "\x0430"+ ,"AElig" * "\x00C6"+ ,"aelig" * "\x00E6"+ ,"AElig;" * "\x00C6"+ ,"aelig;" * "\x00E6"+ ,"af;" * "\x2061"+ ,"Afr;" * "\x1D504"+ ,"afr;" * "\x1D51E"+ ,"Agrave" * "\x00C0"+ ,"agrave" * "\x00E0"+ ,"Agrave;" * "\x00C0"+ ,"agrave;" * "\x00E0"+ ,"alefsym;" * "\x2135"+ ,"aleph;" * "\x2135"+ ,"Alpha;" * "\x0391"+ ,"alpha;" * "\x03B1"+ ,"Amacr;" * "\x0100"+ ,"amacr;" * "\x0101"+ ,"amalg;" * "\x2A3F"+ ,"AMP" * "\x0026"+ ,"amp" * "\x0026"+ ,"AMP;" * "\x0026"+ ,"amp;" * "\x0026"+ ,"and;" * "\x2227"+ ,"And;" * "\x2A53"+ ,"andand;" * "\x2A55"+ ,"andd;" * "\x2A5C"+ ,"andslope;" * "\x2A58"+ ,"andv;" * "\x2A5A"+ ,"ang;" * "\x2220"+ ,"ange;" * "\x29A4"+ ,"angle;" * "\x2220"+ ,"angmsd;" * "\x2221"+ ,"angmsdaa;" * "\x29A8"+ ,"angmsdab;" * "\x29A9"+ ,"angmsdac;" * "\x29AA"+ ,"angmsdad;" * "\x29AB"+ ,"angmsdae;" * "\x29AC"+ ,"angmsdaf;" * "\x29AD"+ ,"angmsdag;" * "\x29AE"+ ,"angmsdah;" * "\x29AF"+ ,"angrt;" * "\x221F"+ ,"angrtvb;" * "\x22BE"+ ,"angrtvbd;" * "\x299D"+ ,"angsph;" * "\x2222"+ ,"angst;" * "\x00C5"+ ,"angzarr;" * "\x237C"+ ,"Aogon;" * "\x0104"+ ,"aogon;" * "\x0105"+ ,"Aopf;" * "\x1D538"+ ,"aopf;" * "\x1D552"+ ,"ap;" * "\x2248"+ ,"apacir;" * "\x2A6F"+ ,"ape;" * "\x224A"+ ,"apE;" * "\x2A70"+ ,"apid;" * "\x224B"+ ,"apos;" * "\x0027"+ ,"ApplyFunction;" * "\x2061"+ ,"approx;" * "\x2248"+ ,"approxeq;" * "\x224A"+ ,"Aring" * "\x00C5"+ ,"aring" * "\x00E5"+ ,"Aring;" * "\x00C5"+ ,"aring;" * "\x00E5"+ ,"Ascr;" * "\x1D49C"+ ,"ascr;" * "\x1D4B6"+ ,"Assign;" * "\x2254"+ ,"ast;" * "\x002A"+ ,"asymp;" * "\x2248"+ ,"asympeq;" * "\x224D"+ ,"Atilde" * "\x00C3"+ ,"atilde" * "\x00E3"+ ,"Atilde;" * "\x00C3"+ ,"atilde;" * "\x00E3"+ ,"Auml" * "\x00C4"+ ,"auml" * "\x00E4"+ ,"Auml;" * "\x00C4"+ ,"auml;" * "\x00E4"+ ,"awconint;" * "\x2233"+ ,"awint;" * "\x2A11"+ ,"backcong;" * "\x224C"+ ,"backepsilon;" * "\x03F6"+ ,"backprime;" * "\x2035"+ ,"backsim;" * "\x223D"+ ,"backsimeq;" * "\x22CD"+ ,"Backslash;" * "\x2216"+ ,"Barv;" * "\x2AE7"+ ,"barvee;" * "\x22BD"+ ,"barwed;" * "\x2305"+ ,"Barwed;" * "\x2306"+ ,"barwedge;" * "\x2305"+ ,"bbrk;" * "\x23B5"+ ,"bbrktbrk;" * "\x23B6"+ ,"bcong;" * "\x224C"+ ,"Bcy;" * "\x0411"+ ,"bcy;" * "\x0431"+ ,"bdquo;" * "\x201E"+ ,"becaus;" * "\x2235"+ ,"Because;" * "\x2235"+ ,"because;" * "\x2235"+ ,"bemptyv;" * "\x29B0"+ ,"bepsi;" * "\x03F6"+ ,"bernou;" * "\x212C"+ ,"Bernoullis;" * "\x212C"+ ,"Beta;" * "\x0392"+ ,"beta;" * "\x03B2"+ ,"beth;" * "\x2136"+ ,"between;" * "\x226C"+ ,"Bfr;" * "\x1D505"+ ,"bfr;" * "\x1D51F"+ ,"bigcap;" * "\x22C2"+ ,"bigcirc;" * "\x25EF"+ ,"bigcup;" * "\x22C3"+ ,"bigodot;" * "\x2A00"+ ,"bigoplus;" * "\x2A01"+ ,"bigotimes;" * "\x2A02"+ ,"bigsqcup;" * "\x2A06"+ ,"bigstar;" * "\x2605"+ ,"bigtriangledown;" * "\x25BD"+ ,"bigtriangleup;" * "\x25B3"+ ,"biguplus;" * "\x2A04"+ ,"bigvee;" * "\x22C1"+ ,"bigwedge;" * "\x22C0"+ ,"bkarow;" * "\x290D"+ ,"blacklozenge;" * "\x29EB"+ ,"blacksquare;" * "\x25AA"+ ,"blacktriangle;" * "\x25B4"+ ,"blacktriangledown;" * "\x25BE"+ ,"blacktriangleleft;" * "\x25C2"+ ,"blacktriangleright;" * "\x25B8"+ ,"blank;" * "\x2423"+ ,"blk12;" * "\x2592"+ ,"blk14;" * "\x2591"+ ,"blk34;" * "\x2593"+ ,"block;" * "\x2588"+ ,"bne;" * "\x003D\x20E5"+ ,"bnequiv;" * "\x2261\x20E5"+ ,"bnot;" * "\x2310"+ ,"bNot;" * "\x2AED"+ ,"Bopf;" * "\x1D539"+ ,"bopf;" * "\x1D553"+ ,"bot;" * "\x22A5"+ ,"bottom;" * "\x22A5"+ ,"bowtie;" * "\x22C8"+ ,"boxbox;" * "\x29C9"+ ,"boxdl;" * "\x2510"+ ,"boxdL;" * "\x2555"+ ,"boxDl;" * "\x2556"+ ,"boxDL;" * "\x2557"+ ,"boxdr;" * "\x250C"+ ,"boxdR;" * "\x2552"+ ,"boxDr;" * "\x2553"+ ,"boxDR;" * "\x2554"+ ,"boxh;" * "\x2500"+ ,"boxH;" * "\x2550"+ ,"boxhd;" * "\x252C"+ ,"boxHd;" * "\x2564"+ ,"boxhD;" * "\x2565"+ ,"boxHD;" * "\x2566"+ ,"boxhu;" * "\x2534"+ ,"boxHu;" * "\x2567"+ ,"boxhU;" * "\x2568"+ ,"boxHU;" * "\x2569"+ ,"boxminus;" * "\x229F"+ ,"boxplus;" * "\x229E"+ ,"boxtimes;" * "\x22A0"+ ,"boxul;" * "\x2518"+ ,"boxuL;" * "\x255B"+ ,"boxUl;" * "\x255C"+ ,"boxUL;" * "\x255D"+ ,"boxur;" * "\x2514"+ ,"boxuR;" * "\x2558"+ ,"boxUr;" * "\x2559"+ ,"boxUR;" * "\x255A"+ ,"boxv;" * "\x2502"+ ,"boxV;" * "\x2551"+ ,"boxvh;" * "\x253C"+ ,"boxvH;" * "\x256A"+ ,"boxVh;" * "\x256B"+ ,"boxVH;" * "\x256C"+ ,"boxvl;" * "\x2524"+ ,"boxvL;" * "\x2561"+ ,"boxVl;" * "\x2562"+ ,"boxVL;" * "\x2563"+ ,"boxvr;" * "\x251C"+ ,"boxvR;" * "\x255E"+ ,"boxVr;" * "\x255F"+ ,"boxVR;" * "\x2560"+ ,"bprime;" * "\x2035"+ ,"Breve;" * "\x02D8"+ ,"breve;" * "\x02D8"+ ,"brvbar" * "\x00A6"+ ,"brvbar;" * "\x00A6"+ ,"Bscr;" * "\x212C"+ ,"bscr;" * "\x1D4B7"+ ,"bsemi;" * "\x204F"+ ,"bsim;" * "\x223D"+ ,"bsime;" * "\x22CD"+ ,"bsol;" * "\x005C"+ ,"bsolb;" * "\x29C5"+ ,"bsolhsub;" * "\x27C8"+ ,"bull;" * "\x2022"+ ,"bullet;" * "\x2022"+ ,"bump;" * "\x224E"+ ,"bumpe;" * "\x224F"+ ,"bumpE;" * "\x2AAE"+ ,"Bumpeq;" * "\x224E"+ ,"bumpeq;" * "\x224F"+ ,"Cacute;" * "\x0106"+ ,"cacute;" * "\x0107"+ ,"cap;" * "\x2229"+ ,"Cap;" * "\x22D2"+ ,"capand;" * "\x2A44"+ ,"capbrcup;" * "\x2A49"+ ,"capcap;" * "\x2A4B"+ ,"capcup;" * "\x2A47"+ ,"capdot;" * "\x2A40"+ ,"CapitalDifferentialD;" * "\x2145"+ ,"caps;" * "\x2229\xFE00"+ ,"caret;" * "\x2041"+ ,"caron;" * "\x02C7"+ ,"Cayleys;" * "\x212D"+ ,"ccaps;" * "\x2A4D"+ ,"Ccaron;" * "\x010C"+ ,"ccaron;" * "\x010D"+ ,"Ccedil" * "\x00C7"+ ,"ccedil" * "\x00E7"+ ,"Ccedil;" * "\x00C7"+ ,"ccedil;" * "\x00E7"+ ,"Ccirc;" * "\x0108"+ ,"ccirc;" * "\x0109"+ ,"Cconint;" * "\x2230"+ ,"ccups;" * "\x2A4C"+ ,"ccupssm;" * "\x2A50"+ ,"Cdot;" * "\x010A"+ ,"cdot;" * "\x010B"+ ,"cedil" * "\x00B8"+ ,"cedil;" * "\x00B8"+ ,"Cedilla;" * "\x00B8"+ ,"cemptyv;" * "\x29B2"+ ,"cent" * "\x00A2"+ ,"cent;" * "\x00A2"+ ,"CenterDot;" * "\x00B7"+ ,"centerdot;" * "\x00B7"+ ,"Cfr;" * "\x212D"+ ,"cfr;" * "\x1D520"+ ,"CHcy;" * "\x0427"+ ,"chcy;" * "\x0447"+ ,"check;" * "\x2713"+ ,"checkmark;" * "\x2713"+ ,"Chi;" * "\x03A7"+ ,"chi;" * "\x03C7"+ ,"cir;" * "\x25CB"+ ,"circ;" * "\x02C6"+ ,"circeq;" * "\x2257"+ ,"circlearrowleft;" * "\x21BA"+ ,"circlearrowright;" * "\x21BB"+ ,"circledast;" * "\x229B"+ ,"circledcirc;" * "\x229A"+ ,"circleddash;" * "\x229D"+ ,"CircleDot;" * "\x2299"+ ,"circledR;" * "\x00AE"+ ,"circledS;" * "\x24C8"+ ,"CircleMinus;" * "\x2296"+ ,"CirclePlus;" * "\x2295"+ ,"CircleTimes;" * "\x2297"+ ,"cire;" * "\x2257"+ ,"cirE;" * "\x29C3"+ ,"cirfnint;" * "\x2A10"+ ,"cirmid;" * "\x2AEF"+ ,"cirscir;" * "\x29C2"+ ,"ClockwiseContourIntegral;" * "\x2232"+ ,"CloseCurlyDoubleQuote;" * "\x201D"+ ,"CloseCurlyQuote;" * "\x2019"+ ,"clubs;" * "\x2663"+ ,"clubsuit;" * "\x2663"+ ,"colon;" * "\x003A"+ ,"Colon;" * "\x2237"+ ,"colone;" * "\x2254"+ ,"Colone;" * "\x2A74"+ ,"coloneq;" * "\x2254"+ ,"comma;" * "\x002C"+ ,"commat;" * "\x0040"+ ,"comp;" * "\x2201"+ ,"compfn;" * "\x2218"+ ,"complement;" * "\x2201"+ ,"complexes;" * "\x2102"+ ,"cong;" * "\x2245"+ ,"congdot;" * "\x2A6D"+ ,"Congruent;" * "\x2261"+ ,"conint;" * "\x222E"+ ,"Conint;" * "\x222F"+ ,"ContourIntegral;" * "\x222E"+ ,"Copf;" * "\x2102"+ ,"copf;" * "\x1D554"+ ,"coprod;" * "\x2210"+ ,"Coproduct;" * "\x2210"+ ,"COPY" * "\x00A9"+ ,"copy" * "\x00A9"+ ,"COPY;" * "\x00A9"+ ,"copy;" * "\x00A9"+ ,"copysr;" * "\x2117"+ ,"CounterClockwiseContourIntegral;" * "\x2233"+ ,"crarr;" * "\x21B5"+ ,"cross;" * "\x2717"+ ,"Cross;" * "\x2A2F"+ ,"Cscr;" * "\x1D49E"+ ,"cscr;" * "\x1D4B8"+ ,"csub;" * "\x2ACF"+ ,"csube;" * "\x2AD1"+ ,"csup;" * "\x2AD0"+ ,"csupe;" * "\x2AD2"+ ,"ctdot;" * "\x22EF"+ ,"cudarrl;" * "\x2938"+ ,"cudarrr;" * "\x2935"+ ,"cuepr;" * "\x22DE"+ ,"cuesc;" * "\x22DF"+ ,"cularr;" * "\x21B6"+ ,"cularrp;" * "\x293D"+ ,"cup;" * "\x222A"+ ,"Cup;" * "\x22D3"+ ,"cupbrcap;" * "\x2A48"+ ,"CupCap;" * "\x224D"+ ,"cupcap;" * "\x2A46"+ ,"cupcup;" * "\x2A4A"+ ,"cupdot;" * "\x228D"+ ,"cupor;" * "\x2A45"+ ,"cups;" * "\x222A\xFE00"+ ,"curarr;" * "\x21B7"+ ,"curarrm;" * "\x293C"+ ,"curlyeqprec;" * "\x22DE"+ ,"curlyeqsucc;" * "\x22DF"+ ,"curlyvee;" * "\x22CE"+ ,"curlywedge;" * "\x22CF"+ ,"curren" * "\x00A4"+ ,"curren;" * "\x00A4"+ ,"curvearrowleft;" * "\x21B6"+ ,"curvearrowright;" * "\x21B7"+ ,"cuvee;" * "\x22CE"+ ,"cuwed;" * "\x22CF"+ ,"cwconint;" * "\x2232"+ ,"cwint;" * "\x2231"+ ,"cylcty;" * "\x232D"+ ,"dagger;" * "\x2020"+ ,"Dagger;" * "\x2021"+ ,"daleth;" * "\x2138"+ ,"darr;" * "\x2193"+ ,"Darr;" * "\x21A1"+ ,"dArr;" * "\x21D3"+ ,"dash;" * "\x2010"+ ,"dashv;" * "\x22A3"+ ,"Dashv;" * "\x2AE4"+ ,"dbkarow;" * "\x290F"+ ,"dblac;" * "\x02DD"+ ,"Dcaron;" * "\x010E"+ ,"dcaron;" * "\x010F"+ ,"Dcy;" * "\x0414"+ ,"dcy;" * "\x0434"+ ,"DD;" * "\x2145"+ ,"dd;" * "\x2146"+ ,"ddagger;" * "\x2021"+ ,"ddarr;" * "\x21CA"+ ,"DDotrahd;" * "\x2911"+ ,"ddotseq;" * "\x2A77"+ ,"deg" * "\x00B0"+ ,"deg;" * "\x00B0"+ ,"Del;" * "\x2207"+ ,"Delta;" * "\x0394"+ ,"delta;" * "\x03B4"+ ,"demptyv;" * "\x29B1"+ ,"dfisht;" * "\x297F"+ ,"Dfr;" * "\x1D507"+ ,"dfr;" * "\x1D521"+ ,"dHar;" * "\x2965"+ ,"dharl;" * "\x21C3"+ ,"dharr;" * "\x21C2"+ ,"DiacriticalAcute;" * "\x00B4"+ ,"DiacriticalDot;" * "\x02D9"+ ,"DiacriticalDoubleAcute;" * "\x02DD"+ ,"DiacriticalGrave;" * "\x0060"+ ,"DiacriticalTilde;" * "\x02DC"+ ,"diam;" * "\x22C4"+ ,"Diamond;" * "\x22C4"+ ,"diamond;" * "\x22C4"+ ,"diamondsuit;" * "\x2666"+ ,"diams;" * "\x2666"+ ,"die;" * "\x00A8"+ ,"DifferentialD;" * "\x2146"+ ,"digamma;" * "\x03DD"+ ,"disin;" * "\x22F2"+ ,"div;" * "\x00F7"+ ,"divide" * "\x00F7"+ ,"divide;" * "\x00F7"+ ,"divideontimes;" * "\x22C7"+ ,"divonx;" * "\x22C7"+ ,"DJcy;" * "\x0402"+ ,"djcy;" * "\x0452"+ ,"dlcorn;" * "\x231E"+ ,"dlcrop;" * "\x230D"+ ,"dollar;" * "\x0024"+ ,"Dopf;" * "\x1D53B"+ ,"dopf;" * "\x1D555"+ ,"Dot;" * "\x00A8"+ ,"dot;" * "\x02D9"+ ,"DotDot;" * "\x20DC"+ ,"doteq;" * "\x2250"+ ,"doteqdot;" * "\x2251"+ ,"DotEqual;" * "\x2250"+ ,"dotminus;" * "\x2238"+ ,"dotplus;" * "\x2214"+ ,"dotsquare;" * "\x22A1"+ ,"doublebarwedge;" * "\x2306"+ ,"DoubleContourIntegral;" * "\x222F"+ ,"DoubleDot;" * "\x00A8"+ ,"DoubleDownArrow;" * "\x21D3"+ ,"DoubleLeftArrow;" * "\x21D0"+ ,"DoubleLeftRightArrow;" * "\x21D4"+ ,"DoubleLeftTee;" * "\x2AE4"+ ,"DoubleLongLeftArrow;" * "\x27F8"+ ,"DoubleLongLeftRightArrow;" * "\x27FA"+ ,"DoubleLongRightArrow;" * "\x27F9"+ ,"DoubleRightArrow;" * "\x21D2"+ ,"DoubleRightTee;" * "\x22A8"+ ,"DoubleUpArrow;" * "\x21D1"+ ,"DoubleUpDownArrow;" * "\x21D5"+ ,"DoubleVerticalBar;" * "\x2225"+ ,"DownArrow;" * "\x2193"+ ,"downarrow;" * "\x2193"+ ,"Downarrow;" * "\x21D3"+ ,"DownArrowBar;" * "\x2913"+ ,"DownArrowUpArrow;" * "\x21F5"+ ,"DownBreve;" * "\x0311"+ ,"downdownarrows;" * "\x21CA"+ ,"downharpoonleft;" * "\x21C3"+ ,"downharpoonright;" * "\x21C2"+ ,"DownLeftRightVector;" * "\x2950"+ ,"DownLeftTeeVector;" * "\x295E"+ ,"DownLeftVector;" * "\x21BD"+ ,"DownLeftVectorBar;" * "\x2956"+ ,"DownRightTeeVector;" * "\x295F"+ ,"DownRightVector;" * "\x21C1"+ ,"DownRightVectorBar;" * "\x2957"+ ,"DownTee;" * "\x22A4"+ ,"DownTeeArrow;" * "\x21A7"+ ,"drbkarow;" * "\x2910"+ ,"drcorn;" * "\x231F"+ ,"drcrop;" * "\x230C"+ ,"Dscr;" * "\x1D49F"+ ,"dscr;" * "\x1D4B9"+ ,"DScy;" * "\x0405"+ ,"dscy;" * "\x0455"+ ,"dsol;" * "\x29F6"+ ,"Dstrok;" * "\x0110"+ ,"dstrok;" * "\x0111"+ ,"dtdot;" * "\x22F1"+ ,"dtri;" * "\x25BF"+ ,"dtrif;" * "\x25BE"+ ,"duarr;" * "\x21F5"+ ,"duhar;" * "\x296F"+ ,"dwangle;" * "\x29A6"+ ,"DZcy;" * "\x040F"+ ,"dzcy;" * "\x045F"+ ,"dzigrarr;" * "\x27FF"+ ,"Eacute" * "\x00C9"+ ,"eacute" * "\x00E9"+ ,"Eacute;" * "\x00C9"+ ,"eacute;" * "\x00E9"+ ,"easter;" * "\x2A6E"+ ,"Ecaron;" * "\x011A"+ ,"ecaron;" * "\x011B"+ ,"ecir;" * "\x2256"+ ,"Ecirc" * "\x00CA"+ ,"ecirc" * "\x00EA"+ ,"Ecirc;" * "\x00CA"+ ,"ecirc;" * "\x00EA"+ ,"ecolon;" * "\x2255"+ ,"Ecy;" * "\x042D"+ ,"ecy;" * "\x044D"+ ,"eDDot;" * "\x2A77"+ ,"Edot;" * "\x0116"+ ,"edot;" * "\x0117"+ ,"eDot;" * "\x2251"+ ,"ee;" * "\x2147"+ ,"efDot;" * "\x2252"+ ,"Efr;" * "\x1D508"+ ,"efr;" * "\x1D522"+ ,"eg;" * "\x2A9A"+ ,"Egrave" * "\x00C8"+ ,"egrave" * "\x00E8"+ ,"Egrave;" * "\x00C8"+ ,"egrave;" * "\x00E8"+ ,"egs;" * "\x2A96"+ ,"egsdot;" * "\x2A98"+ ,"el;" * "\x2A99"+ ,"Element;" * "\x2208"+ ,"elinters;" * "\x23E7"+ ,"ell;" * "\x2113"+ ,"els;" * "\x2A95"+ ,"elsdot;" * "\x2A97"+ ,"Emacr;" * "\x0112"+ ,"emacr;" * "\x0113"+ ,"empty;" * "\x2205"+ ,"emptyset;" * "\x2205"+ ,"EmptySmallSquare;" * "\x25FB"+ ,"emptyv;" * "\x2205"+ ,"EmptyVerySmallSquare;" * "\x25AB"+ ,"emsp13;" * "\x2004"+ ,"emsp14;" * "\x2005"+ ,"emsp;" * "\x2003"+ ,"ENG;" * "\x014A"+ ,"eng;" * "\x014B"+ ,"ensp;" * "\x2002"+ ,"Eogon;" * "\x0118"+ ,"eogon;" * "\x0119"+ ,"Eopf;" * "\x1D53C"+ ,"eopf;" * "\x1D556"+ ,"epar;" * "\x22D5"+ ,"eparsl;" * "\x29E3"+ ,"eplus;" * "\x2A71"+ ,"epsi;" * "\x03B5"+ ,"Epsilon;" * "\x0395"+ ,"epsilon;" * "\x03B5"+ ,"epsiv;" * "\x03F5"+ ,"eqcirc;" * "\x2256"+ ,"eqcolon;" * "\x2255"+ ,"eqsim;" * "\x2242"+ ,"eqslantgtr;" * "\x2A96"+ ,"eqslantless;" * "\x2A95"+ ,"Equal;" * "\x2A75"+ ,"equals;" * "\x003D"+ ,"EqualTilde;" * "\x2242"+ ,"equest;" * "\x225F"+ ,"Equilibrium;" * "\x21CC"+ ,"equiv;" * "\x2261"+ ,"equivDD;" * "\x2A78"+ ,"eqvparsl;" * "\x29E5"+ ,"erarr;" * "\x2971"+ ,"erDot;" * "\x2253"+ ,"escr;" * "\x212F"+ ,"Escr;" * "\x2130"+ ,"esdot;" * "\x2250"+ ,"esim;" * "\x2242"+ ,"Esim;" * "\x2A73"+ ,"Eta;" * "\x0397"+ ,"eta;" * "\x03B7"+ ,"ETH" * "\x00D0"+ ,"eth" * "\x00F0"+ ,"ETH;" * "\x00D0"+ ,"eth;" * "\x00F0"+ ,"Euml" * "\x00CB"+ ,"euml" * "\x00EB"+ ,"Euml;" * "\x00CB"+ ,"euml;" * "\x00EB"+ ,"euro;" * "\x20AC"+ ,"excl;" * "\x0021"+ ,"exist;" * "\x2203"+ ,"Exists;" * "\x2203"+ ,"expectation;" * "\x2130"+ ,"ExponentialE;" * "\x2147"+ ,"exponentiale;" * "\x2147"+ ,"fallingdotseq;" * "\x2252"+ ,"Fcy;" * "\x0424"+ ,"fcy;" * "\x0444"+ ,"female;" * "\x2640"+ ,"ffilig;" * "\xFB03"+ ,"fflig;" * "\xFB00"+ ,"ffllig;" * "\xFB04"+ ,"Ffr;" * "\x1D509"+ ,"ffr;" * "\x1D523"+ ,"filig;" * "\xFB01"+ ,"FilledSmallSquare;" * "\x25FC"+ ,"FilledVerySmallSquare;" * "\x25AA"+ ,"fjlig;" * "\x0066\x006A"+ ,"flat;" * "\x266D"+ ,"fllig;" * "\xFB02"+ ,"fltns;" * "\x25B1"+ ,"fnof;" * "\x0192"+ ,"Fopf;" * "\x1D53D"+ ,"fopf;" * "\x1D557"+ ,"ForAll;" * "\x2200"+ ,"forall;" * "\x2200"+ ,"fork;" * "\x22D4"+ ,"forkv;" * "\x2AD9"+ ,"Fouriertrf;" * "\x2131"+ ,"fpartint;" * "\x2A0D"+ ,"frac12" * "\x00BD"+ ,"frac12;" * "\x00BD"+ ,"frac13;" * "\x2153"+ ,"frac14" * "\x00BC"+ ,"frac14;" * "\x00BC"+ ,"frac15;" * "\x2155"+ ,"frac16;" * "\x2159"+ ,"frac18;" * "\x215B"+ ,"frac23;" * "\x2154"+ ,"frac25;" * "\x2156"+ ,"frac34" * "\x00BE"+ ,"frac34;" * "\x00BE"+ ,"frac35;" * "\x2157"+ ,"frac38;" * "\x215C"+ ,"frac45;" * "\x2158"+ ,"frac56;" * "\x215A"+ ,"frac58;" * "\x215D"+ ,"frac78;" * "\x215E"+ ,"frasl;" * "\x2044"+ ,"frown;" * "\x2322"+ ,"Fscr;" * "\x2131"+ ,"fscr;" * "\x1D4BB"+ ,"gacute;" * "\x01F5"+ ,"Gamma;" * "\x0393"+ ,"gamma;" * "\x03B3"+ ,"Gammad;" * "\x03DC"+ ,"gammad;" * "\x03DD"+ ,"gap;" * "\x2A86"+ ,"Gbreve;" * "\x011E"+ ,"gbreve;" * "\x011F"+ ,"Gcedil;" * "\x0122"+ ,"Gcirc;" * "\x011C"+ ,"gcirc;" * "\x011D"+ ,"Gcy;" * "\x0413"+ ,"gcy;" * "\x0433"+ ,"Gdot;" * "\x0120"+ ,"gdot;" * "\x0121"+ ,"ge;" * "\x2265"+ ,"gE;" * "\x2267"+ ,"gel;" * "\x22DB"+ ,"gEl;" * "\x2A8C"+ ,"geq;" * "\x2265"+ ,"geqq;" * "\x2267"+ ,"geqslant;" * "\x2A7E"+ ,"ges;" * "\x2A7E"+ ,"gescc;" * "\x2AA9"+ ,"gesdot;" * "\x2A80"+ ,"gesdoto;" * "\x2A82"+ ,"gesdotol;" * "\x2A84"+ ,"gesl;" * "\x22DB\xFE00"+ ,"gesles;" * "\x2A94"+ ,"Gfr;" * "\x1D50A"+ ,"gfr;" * "\x1D524"+ ,"gg;" * "\x226B"+ ,"Gg;" * "\x22D9"+ ,"ggg;" * "\x22D9"+ ,"gimel;" * "\x2137"+ ,"GJcy;" * "\x0403"+ ,"gjcy;" * "\x0453"+ ,"gl;" * "\x2277"+ ,"gla;" * "\x2AA5"+ ,"glE;" * "\x2A92"+ ,"glj;" * "\x2AA4"+ ,"gnap;" * "\x2A8A"+ ,"gnapprox;" * "\x2A8A"+ ,"gnE;" * "\x2269"+ ,"gne;" * "\x2A88"+ ,"gneq;" * "\x2A88"+ ,"gneqq;" * "\x2269"+ ,"gnsim;" * "\x22E7"+ ,"Gopf;" * "\x1D53E"+ ,"gopf;" * "\x1D558"+ ,"grave;" * "\x0060"+ ,"GreaterEqual;" * "\x2265"+ ,"GreaterEqualLess;" * "\x22DB"+ ,"GreaterFullEqual;" * "\x2267"+ ,"GreaterGreater;" * "\x2AA2"+ ,"GreaterLess;" * "\x2277"+ ,"GreaterSlantEqual;" * "\x2A7E"+ ,"GreaterTilde;" * "\x2273"+ ,"gscr;" * "\x210A"+ ,"Gscr;" * "\x1D4A2"+ ,"gsim;" * "\x2273"+ ,"gsime;" * "\x2A8E"+ ,"gsiml;" * "\x2A90"+ ,"GT" * "\x003E"+ ,"gt" * "\x003E"+ ,"GT;" * "\x003E"+ ,"gt;" * "\x003E"+ ,"Gt;" * "\x226B"+ ,"gtcc;" * "\x2AA7"+ ,"gtcir;" * "\x2A7A"+ ,"gtdot;" * "\x22D7"+ ,"gtlPar;" * "\x2995"+ ,"gtquest;" * "\x2A7C"+ ,"gtrapprox;" * "\x2A86"+ ,"gtrarr;" * "\x2978"+ ,"gtrdot;" * "\x22D7"+ ,"gtreqless;" * "\x22DB"+ ,"gtreqqless;" * "\x2A8C"+ ,"gtrless;" * "\x2277"+ ,"gtrsim;" * "\x2273"+ ,"gvertneqq;" * "\x2269\xFE00"+ ,"gvnE;" * "\x2269\xFE00"+ ,"Hacek;" * "\x02C7"+ ,"hairsp;" * "\x200A"+ ,"half;" * "\x00BD"+ ,"hamilt;" * "\x210B"+ ,"HARDcy;" * "\x042A"+ ,"hardcy;" * "\x044A"+ ,"harr;" * "\x2194"+ ,"hArr;" * "\x21D4"+ ,"harrcir;" * "\x2948"+ ,"harrw;" * "\x21AD"+ ,"Hat;" * "\x005E"+ ,"hbar;" * "\x210F"+ ,"Hcirc;" * "\x0124"+ ,"hcirc;" * "\x0125"+ ,"hearts;" * "\x2665"+ ,"heartsuit;" * "\x2665"+ ,"hellip;" * "\x2026"+ ,"hercon;" * "\x22B9"+ ,"Hfr;" * "\x210C"+ ,"hfr;" * "\x1D525"+ ,"HilbertSpace;" * "\x210B"+ ,"hksearow;" * "\x2925"+ ,"hkswarow;" * "\x2926"+ ,"hoarr;" * "\x21FF"+ ,"homtht;" * "\x223B"+ ,"hookleftarrow;" * "\x21A9"+ ,"hookrightarrow;" * "\x21AA"+ ,"Hopf;" * "\x210D"+ ,"hopf;" * "\x1D559"+ ,"horbar;" * "\x2015"+ ,"HorizontalLine;" * "\x2500"+ ,"Hscr;" * "\x210B"+ ,"hscr;" * "\x1D4BD"+ ,"hslash;" * "\x210F"+ ,"Hstrok;" * "\x0126"+ ,"hstrok;" * "\x0127"+ ,"HumpDownHump;" * "\x224E"+ ,"HumpEqual;" * "\x224F"+ ,"hybull;" * "\x2043"+ ,"hyphen;" * "\x2010"+ ,"Iacute" * "\x00CD"+ ,"iacute" * "\x00ED"+ ,"Iacute;" * "\x00CD"+ ,"iacute;" * "\x00ED"+ ,"ic;" * "\x2063"+ ,"Icirc" * "\x00CE"+ ,"icirc" * "\x00EE"+ ,"Icirc;" * "\x00CE"+ ,"icirc;" * "\x00EE"+ ,"Icy;" * "\x0418"+ ,"icy;" * "\x0438"+ ,"Idot;" * "\x0130"+ ,"IEcy;" * "\x0415"+ ,"iecy;" * "\x0435"+ ,"iexcl" * "\x00A1"+ ,"iexcl;" * "\x00A1"+ ,"iff;" * "\x21D4"+ ,"Ifr;" * "\x2111"+ ,"ifr;" * "\x1D526"+ ,"Igrave" * "\x00CC"+ ,"igrave" * "\x00EC"+ ,"Igrave;" * "\x00CC"+ ,"igrave;" * "\x00EC"+ ,"ii;" * "\x2148"+ ,"iiiint;" * "\x2A0C"+ ,"iiint;" * "\x222D"+ ,"iinfin;" * "\x29DC"+ ,"iiota;" * "\x2129"+ ,"IJlig;" * "\x0132"+ ,"ijlig;" * "\x0133"+ ,"Im;" * "\x2111"+ ,"Imacr;" * "\x012A"+ ,"imacr;" * "\x012B"+ ,"image;" * "\x2111"+ ,"ImaginaryI;" * "\x2148"+ ,"imagline;" * "\x2110"+ ,"imagpart;" * "\x2111"+ ,"imath;" * "\x0131"+ ,"imof;" * "\x22B7"+ ,"imped;" * "\x01B5"+ ,"Implies;" * "\x21D2"+ ,"in;" * "\x2208"+ ,"incare;" * "\x2105"+ ,"infin;" * "\x221E"+ ,"infintie;" * "\x29DD"+ ,"inodot;" * "\x0131"+ ,"int;" * "\x222B"+ ,"Int;" * "\x222C"+ ,"intcal;" * "\x22BA"+ ,"integers;" * "\x2124"+ ,"Integral;" * "\x222B"+ ,"intercal;" * "\x22BA"+ ,"Intersection;" * "\x22C2"+ ,"intlarhk;" * "\x2A17"+ ,"intprod;" * "\x2A3C"+ ,"InvisibleComma;" * "\x2063"+ ,"InvisibleTimes;" * "\x2062"+ ,"IOcy;" * "\x0401"+ ,"iocy;" * "\x0451"+ ,"Iogon;" * "\x012E"+ ,"iogon;" * "\x012F"+ ,"Iopf;" * "\x1D540"+ ,"iopf;" * "\x1D55A"+ ,"Iota;" * "\x0399"+ ,"iota;" * "\x03B9"+ ,"iprod;" * "\x2A3C"+ ,"iquest" * "\x00BF"+ ,"iquest;" * "\x00BF"+ ,"Iscr;" * "\x2110"+ ,"iscr;" * "\x1D4BE"+ ,"isin;" * "\x2208"+ ,"isindot;" * "\x22F5"+ ,"isinE;" * "\x22F9"+ ,"isins;" * "\x22F4"+ ,"isinsv;" * "\x22F3"+ ,"isinv;" * "\x2208"+ ,"it;" * "\x2062"+ ,"Itilde;" * "\x0128"+ ,"itilde;" * "\x0129"+ ,"Iukcy;" * "\x0406"+ ,"iukcy;" * "\x0456"+ ,"Iuml" * "\x00CF"+ ,"iuml" * "\x00EF"+ ,"Iuml;" * "\x00CF"+ ,"iuml;" * "\x00EF"+ ,"Jcirc;" * "\x0134"+ ,"jcirc;" * "\x0135"+ ,"Jcy;" * "\x0419"+ ,"jcy;" * "\x0439"+ ,"Jfr;" * "\x1D50D"+ ,"jfr;" * "\x1D527"+ ,"jmath;" * "\x0237"+ ,"Jopf;" * "\x1D541"+ ,"jopf;" * "\x1D55B"+ ,"Jscr;" * "\x1D4A5"+ ,"jscr;" * "\x1D4BF"+ ,"Jsercy;" * "\x0408"+ ,"jsercy;" * "\x0458"+ ,"Jukcy;" * "\x0404"+ ,"jukcy;" * "\x0454"+ ,"Kappa;" * "\x039A"+ ,"kappa;" * "\x03BA"+ ,"kappav;" * "\x03F0"+ ,"Kcedil;" * "\x0136"+ ,"kcedil;" * "\x0137"+ ,"Kcy;" * "\x041A"+ ,"kcy;" * "\x043A"+ ,"Kfr;" * "\x1D50E"+ ,"kfr;" * "\x1D528"+ ,"kgreen;" * "\x0138"+ ,"KHcy;" * "\x0425"+ ,"khcy;" * "\x0445"+ ,"KJcy;" * "\x040C"+ ,"kjcy;" * "\x045C"+ ,"Kopf;" * "\x1D542"+ ,"kopf;" * "\x1D55C"+ ,"Kscr;" * "\x1D4A6"+ ,"kscr;" * "\x1D4C0"+ ,"lAarr;" * "\x21DA"+ ,"Lacute;" * "\x0139"+ ,"lacute;" * "\x013A"+ ,"laemptyv;" * "\x29B4"+ ,"lagran;" * "\x2112"+ ,"Lambda;" * "\x039B"+ ,"lambda;" * "\x03BB"+ ,"lang;" * "\x27E8"+ ,"Lang;" * "\x27EA"+ ,"langd;" * "\x2991"+ ,"langle;" * "\x27E8"+ ,"lap;" * "\x2A85"+ ,"Laplacetrf;" * "\x2112"+ ,"laquo" * "\x00AB"+ ,"laquo;" * "\x00AB"+ ,"larr;" * "\x2190"+ ,"Larr;" * "\x219E"+ ,"lArr;" * "\x21D0"+ ,"larrb;" * "\x21E4"+ ,"larrbfs;" * "\x291F"+ ,"larrfs;" * "\x291D"+ ,"larrhk;" * "\x21A9"+ ,"larrlp;" * "\x21AB"+ ,"larrpl;" * "\x2939"+ ,"larrsim;" * "\x2973"+ ,"larrtl;" * "\x21A2"+ ,"lat;" * "\x2AAB"+ ,"latail;" * "\x2919"+ ,"lAtail;" * "\x291B"+ ,"late;" * "\x2AAD"+ ,"lates;" * "\x2AAD\xFE00"+ ,"lbarr;" * "\x290C"+ ,"lBarr;" * "\x290E"+ ,"lbbrk;" * "\x2772"+ ,"lbrace;" * "\x007B"+ ,"lbrack;" * "\x005B"+ ,"lbrke;" * "\x298B"+ ,"lbrksld;" * "\x298F"+ ,"lbrkslu;" * "\x298D"+ ,"Lcaron;" * "\x013D"+ ,"lcaron;" * "\x013E"+ ,"Lcedil;" * "\x013B"+ ,"lcedil;" * "\x013C"+ ,"lceil;" * "\x2308"+ ,"lcub;" * "\x007B"+ ,"Lcy;" * "\x041B"+ ,"lcy;" * "\x043B"+ ,"ldca;" * "\x2936"+ ,"ldquo;" * "\x201C"+ ,"ldquor;" * "\x201E"+ ,"ldrdhar;" * "\x2967"+ ,"ldrushar;" * "\x294B"+ ,"ldsh;" * "\x21B2"+ ,"le;" * "\x2264"+ ,"lE;" * "\x2266"+ ,"LeftAngleBracket;" * "\x27E8"+ ,"LeftArrow;" * "\x2190"+ ,"leftarrow;" * "\x2190"+ ,"Leftarrow;" * "\x21D0"+ ,"LeftArrowBar;" * "\x21E4"+ ,"LeftArrowRightArrow;" * "\x21C6"+ ,"leftarrowtail;" * "\x21A2"+ ,"LeftCeiling;" * "\x2308"+ ,"LeftDoubleBracket;" * "\x27E6"+ ,"LeftDownTeeVector;" * "\x2961"+ ,"LeftDownVector;" * "\x21C3"+ ,"LeftDownVectorBar;" * "\x2959"+ ,"LeftFloor;" * "\x230A"+ ,"leftharpoondown;" * "\x21BD"+ ,"leftharpoonup;" * "\x21BC"+ ,"leftleftarrows;" * "\x21C7"+ ,"LeftRightArrow;" * "\x2194"+ ,"leftrightarrow;" * "\x2194"+ ,"Leftrightarrow;" * "\x21D4"+ ,"leftrightarrows;" * "\x21C6"+ ,"leftrightharpoons;" * "\x21CB"+ ,"leftrightsquigarrow;" * "\x21AD"+ ,"LeftRightVector;" * "\x294E"+ ,"LeftTee;" * "\x22A3"+ ,"LeftTeeArrow;" * "\x21A4"+ ,"LeftTeeVector;" * "\x295A"+ ,"leftthreetimes;" * "\x22CB"+ ,"LeftTriangle;" * "\x22B2"+ ,"LeftTriangleBar;" * "\x29CF"+ ,"LeftTriangleEqual;" * "\x22B4"+ ,"LeftUpDownVector;" * "\x2951"+ ,"LeftUpTeeVector;" * "\x2960"+ ,"LeftUpVector;" * "\x21BF"+ ,"LeftUpVectorBar;" * "\x2958"+ ,"LeftVector;" * "\x21BC"+ ,"LeftVectorBar;" * "\x2952"+ ,"leg;" * "\x22DA"+ ,"lEg;" * "\x2A8B"+ ,"leq;" * "\x2264"+ ,"leqq;" * "\x2266"+ ,"leqslant;" * "\x2A7D"+ ,"les;" * "\x2A7D"+ ,"lescc;" * "\x2AA8"+ ,"lesdot;" * "\x2A7F"+ ,"lesdoto;" * "\x2A81"+ ,"lesdotor;" * "\x2A83"+ ,"lesg;" * "\x22DA\xFE00"+ ,"lesges;" * "\x2A93"+ ,"lessapprox;" * "\x2A85"+ ,"lessdot;" * "\x22D6"+ ,"lesseqgtr;" * "\x22DA"+ ,"lesseqqgtr;" * "\x2A8B"+ ,"LessEqualGreater;" * "\x22DA"+ ,"LessFullEqual;" * "\x2266"+ ,"LessGreater;" * "\x2276"+ ,"lessgtr;" * "\x2276"+ ,"LessLess;" * "\x2AA1"+ ,"lesssim;" * "\x2272"+ ,"LessSlantEqual;" * "\x2A7D"+ ,"LessTilde;" * "\x2272"+ ,"lfisht;" * "\x297C"+ ,"lfloor;" * "\x230A"+ ,"Lfr;" * "\x1D50F"+ ,"lfr;" * "\x1D529"+ ,"lg;" * "\x2276"+ ,"lgE;" * "\x2A91"+ ,"lHar;" * "\x2962"+ ,"lhard;" * "\x21BD"+ ,"lharu;" * "\x21BC"+ ,"lharul;" * "\x296A"+ ,"lhblk;" * "\x2584"+ ,"LJcy;" * "\x0409"+ ,"ljcy;" * "\x0459"+ ,"ll;" * "\x226A"+ ,"Ll;" * "\x22D8"+ ,"llarr;" * "\x21C7"+ ,"llcorner;" * "\x231E"+ ,"Lleftarrow;" * "\x21DA"+ ,"llhard;" * "\x296B"+ ,"lltri;" * "\x25FA"+ ,"Lmidot;" * "\x013F"+ ,"lmidot;" * "\x0140"+ ,"lmoust;" * "\x23B0"+ ,"lmoustache;" * "\x23B0"+ ,"lnap;" * "\x2A89"+ ,"lnapprox;" * "\x2A89"+ ,"lnE;" * "\x2268"+ ,"lne;" * "\x2A87"+ ,"lneq;" * "\x2A87"+ ,"lneqq;" * "\x2268"+ ,"lnsim;" * "\x22E6"+ ,"loang;" * "\x27EC"+ ,"loarr;" * "\x21FD"+ ,"lobrk;" * "\x27E6"+ ,"LongLeftArrow;" * "\x27F5"+ ,"longleftarrow;" * "\x27F5"+ ,"Longleftarrow;" * "\x27F8"+ ,"LongLeftRightArrow;" * "\x27F7"+ ,"longleftrightarrow;" * "\x27F7"+ ,"Longleftrightarrow;" * "\x27FA"+ ,"longmapsto;" * "\x27FC"+ ,"LongRightArrow;" * "\x27F6"+ ,"longrightarrow;" * "\x27F6"+ ,"Longrightarrow;" * "\x27F9"+ ,"looparrowleft;" * "\x21AB"+ ,"looparrowright;" * "\x21AC"+ ,"lopar;" * "\x2985"+ ,"Lopf;" * "\x1D543"+ ,"lopf;" * "\x1D55D"+ ,"loplus;" * "\x2A2D"+ ,"lotimes;" * "\x2A34"+ ,"lowast;" * "\x2217"+ ,"lowbar;" * "\x005F"+ ,"LowerLeftArrow;" * "\x2199"+ ,"LowerRightArrow;" * "\x2198"+ ,"loz;" * "\x25CA"+ ,"lozenge;" * "\x25CA"+ ,"lozf;" * "\x29EB"+ ,"lpar;" * "\x0028"+ ,"lparlt;" * "\x2993"+ ,"lrarr;" * "\x21C6"+ ,"lrcorner;" * "\x231F"+ ,"lrhar;" * "\x21CB"+ ,"lrhard;" * "\x296D"+ ,"lrm;" * "\x200E"+ ,"lrtri;" * "\x22BF"+ ,"lsaquo;" * "\x2039"+ ,"Lscr;" * "\x2112"+ ,"lscr;" * "\x1D4C1"+ ,"Lsh;" * "\x21B0"+ ,"lsh;" * "\x21B0"+ ,"lsim;" * "\x2272"+ ,"lsime;" * "\x2A8D"+ ,"lsimg;" * "\x2A8F"+ ,"lsqb;" * "\x005B"+ ,"lsquo;" * "\x2018"+ ,"lsquor;" * "\x201A"+ ,"Lstrok;" * "\x0141"+ ,"lstrok;" * "\x0142"+ ,"LT" * "\x003C"+ ,"lt" * "\x003C"+ ,"LT;" * "\x003C"+ ,"lt;" * "\x003C"+ ,"Lt;" * "\x226A"+ ,"ltcc;" * "\x2AA6"+ ,"ltcir;" * "\x2A79"+ ,"ltdot;" * "\x22D6"+ ,"lthree;" * "\x22CB"+ ,"ltimes;" * "\x22C9"+ ,"ltlarr;" * "\x2976"+ ,"ltquest;" * "\x2A7B"+ ,"ltri;" * "\x25C3"+ ,"ltrie;" * "\x22B4"+ ,"ltrif;" * "\x25C2"+ ,"ltrPar;" * "\x2996"+ ,"lurdshar;" * "\x294A"+ ,"luruhar;" * "\x2966"+ ,"lvertneqq;" * "\x2268\xFE00"+ ,"lvnE;" * "\x2268\xFE00"+ ,"macr" * "\x00AF"+ ,"macr;" * "\x00AF"+ ,"male;" * "\x2642"+ ,"malt;" * "\x2720"+ ,"maltese;" * "\x2720"+ ,"map;" * "\x21A6"+ ,"Map;" * "\x2905"+ ,"mapsto;" * "\x21A6"+ ,"mapstodown;" * "\x21A7"+ ,"mapstoleft;" * "\x21A4"+ ,"mapstoup;" * "\x21A5"+ ,"marker;" * "\x25AE"+ ,"mcomma;" * "\x2A29"+ ,"Mcy;" * "\x041C"+ ,"mcy;" * "\x043C"+ ,"mdash;" * "\x2014"+ ,"mDDot;" * "\x223A"+ ,"measuredangle;" * "\x2221"+ ,"MediumSpace;" * "\x205F"+ ,"Mellintrf;" * "\x2133"+ ,"Mfr;" * "\x1D510"+ ,"mfr;" * "\x1D52A"+ ,"mho;" * "\x2127"+ ,"micro" * "\x00B5"+ ,"micro;" * "\x00B5"+ ,"mid;" * "\x2223"+ ,"midast;" * "\x002A"+ ,"midcir;" * "\x2AF0"+ ,"middot" * "\x00B7"+ ,"middot;" * "\x00B7"+ ,"minus;" * "\x2212"+ ,"minusb;" * "\x229F"+ ,"minusd;" * "\x2238"+ ,"minusdu;" * "\x2A2A"+ ,"MinusPlus;" * "\x2213"+ ,"mlcp;" * "\x2ADB"+ ,"mldr;" * "\x2026"+ ,"mnplus;" * "\x2213"+ ,"models;" * "\x22A7"+ ,"Mopf;" * "\x1D544"+ ,"mopf;" * "\x1D55E"+ ,"mp;" * "\x2213"+ ,"Mscr;" * "\x2133"+ ,"mscr;" * "\x1D4C2"+ ,"mstpos;" * "\x223E"+ ,"Mu;" * "\x039C"+ ,"mu;" * "\x03BC"+ ,"multimap;" * "\x22B8"+ ,"mumap;" * "\x22B8"+ ,"nabla;" * "\x2207"+ ,"Nacute;" * "\x0143"+ ,"nacute;" * "\x0144"+ ,"nang;" * "\x2220\x20D2"+ ,"nap;" * "\x2249"+ ,"napE;" * "\x2A70\x0338"+ ,"napid;" * "\x224B\x0338"+ ,"napos;" * "\x0149"+ ,"napprox;" * "\x2249"+ ,"natur;" * "\x266E"+ ,"natural;" * "\x266E"+ ,"naturals;" * "\x2115"+ ,"nbsp" * "\x00A0"+ ,"nbsp;" * "\x00A0"+ ,"nbump;" * "\x224E\x0338"+ ,"nbumpe;" * "\x224F\x0338"+ ,"ncap;" * "\x2A43"+ ,"Ncaron;" * "\x0147"+ ,"ncaron;" * "\x0148"+ ,"Ncedil;" * "\x0145"+ ,"ncedil;" * "\x0146"+ ,"ncong;" * "\x2247"+ ,"ncongdot;" * "\x2A6D\x0338"+ ,"ncup;" * "\x2A42"+ ,"Ncy;" * "\x041D"+ ,"ncy;" * "\x043D"+ ,"ndash;" * "\x2013"+ ,"ne;" * "\x2260"+ ,"nearhk;" * "\x2924"+ ,"nearr;" * "\x2197"+ ,"neArr;" * "\x21D7"+ ,"nearrow;" * "\x2197"+ ,"nedot;" * "\x2250\x0338"+ ,"NegativeMediumSpace;" * "\x200B"+ ,"NegativeThickSpace;" * "\x200B"+ ,"NegativeThinSpace;" * "\x200B"+ ,"NegativeVeryThinSpace;" * "\x200B"+ ,"nequiv;" * "\x2262"+ ,"nesear;" * "\x2928"+ ,"nesim;" * "\x2242\x0338"+ ,"NestedGreaterGreater;" * "\x226B"+ ,"NestedLessLess;" * "\x226A"+ ,"NewLine;" * "\x000A"+ ,"nexist;" * "\x2204"+ ,"nexists;" * "\x2204"+ ,"Nfr;" * "\x1D511"+ ,"nfr;" * "\x1D52B"+ ,"ngE;" * "\x2267\x0338"+ ,"nge;" * "\x2271"+ ,"ngeq;" * "\x2271"+ ,"ngeqq;" * "\x2267\x0338"+ ,"ngeqslant;" * "\x2A7E\x0338"+ ,"nges;" * "\x2A7E\x0338"+ ,"nGg;" * "\x22D9\x0338"+ ,"ngsim;" * "\x2275"+ ,"nGt;" * "\x226B\x20D2"+ ,"ngt;" * "\x226F"+ ,"ngtr;" * "\x226F"+ ,"nGtv;" * "\x226B\x0338"+ ,"nharr;" * "\x21AE"+ ,"nhArr;" * "\x21CE"+ ,"nhpar;" * "\x2AF2"+ ,"ni;" * "\x220B"+ ,"nis;" * "\x22FC"+ ,"nisd;" * "\x22FA"+ ,"niv;" * "\x220B"+ ,"NJcy;" * "\x040A"+ ,"njcy;" * "\x045A"+ ,"nlarr;" * "\x219A"+ ,"nlArr;" * "\x21CD"+ ,"nldr;" * "\x2025"+ ,"nlE;" * "\x2266\x0338"+ ,"nle;" * "\x2270"+ ,"nleftarrow;" * "\x219A"+ ,"nLeftarrow;" * "\x21CD"+ ,"nleftrightarrow;" * "\x21AE"+ ,"nLeftrightarrow;" * "\x21CE"+ ,"nleq;" * "\x2270"+ ,"nleqq;" * "\x2266\x0338"+ ,"nleqslant;" * "\x2A7D\x0338"+ ,"nles;" * "\x2A7D\x0338"+ ,"nless;" * "\x226E"+ ,"nLl;" * "\x22D8\x0338"+ ,"nlsim;" * "\x2274"+ ,"nLt;" * "\x226A\x20D2"+ ,"nlt;" * "\x226E"+ ,"nltri;" * "\x22EA"+ ,"nltrie;" * "\x22EC"+ ,"nLtv;" * "\x226A\x0338"+ ,"nmid;" * "\x2224"+ ,"NoBreak;" * "\x2060"+ ,"NonBreakingSpace;" * "\x00A0"+ ,"Nopf;" * "\x2115"+ ,"nopf;" * "\x1D55F"+ ,"not" * "\x00AC"+ ,"not;" * "\x00AC"+ ,"Not;" * "\x2AEC"+ ,"NotCongruent;" * "\x2262"+ ,"NotCupCap;" * "\x226D"+ ,"NotDoubleVerticalBar;" * "\x2226"+ ,"NotElement;" * "\x2209"+ ,"NotEqual;" * "\x2260"+ ,"NotEqualTilde;" * "\x2242\x0338"+ ,"NotExists;" * "\x2204"+ ,"NotGreater;" * "\x226F"+ ,"NotGreaterEqual;" * "\x2271"+ ,"NotGreaterFullEqual;" * "\x2267\x0338"+ ,"NotGreaterGreater;" * "\x226B\x0338"+ ,"NotGreaterLess;" * "\x2279"+ ,"NotGreaterSlantEqual;" * "\x2A7E\x0338"+ ,"NotGreaterTilde;" * "\x2275"+ ,"NotHumpDownHump;" * "\x224E\x0338"+ ,"NotHumpEqual;" * "\x224F\x0338"+ ,"notin;" * "\x2209"+ ,"notindot;" * "\x22F5\x0338"+ ,"notinE;" * "\x22F9\x0338"+ ,"notinva;" * "\x2209"+ ,"notinvb;" * "\x22F7"+ ,"notinvc;" * "\x22F6"+ ,"NotLeftTriangle;" * "\x22EA"+ ,"NotLeftTriangleBar;" * "\x29CF\x0338"+ ,"NotLeftTriangleEqual;" * "\x22EC"+ ,"NotLess;" * "\x226E"+ ,"NotLessEqual;" * "\x2270"+ ,"NotLessGreater;" * "\x2278"+ ,"NotLessLess;" * "\x226A\x0338"+ ,"NotLessSlantEqual;" * "\x2A7D\x0338"+ ,"NotLessTilde;" * "\x2274"+ ,"NotNestedGreaterGreater;" * "\x2AA2\x0338"+ ,"NotNestedLessLess;" * "\x2AA1\x0338"+ ,"notni;" * "\x220C"+ ,"notniva;" * "\x220C"+ ,"notnivb;" * "\x22FE"+ ,"notnivc;" * "\x22FD"+ ,"NotPrecedes;" * "\x2280"+ ,"NotPrecedesEqual;" * "\x2AAF\x0338"+ ,"NotPrecedesSlantEqual;" * "\x22E0"+ ,"NotReverseElement;" * "\x220C"+ ,"NotRightTriangle;" * "\x22EB"+ ,"NotRightTriangleBar;" * "\x29D0\x0338"+ ,"NotRightTriangleEqual;" * "\x22ED"+ ,"NotSquareSubset;" * "\x228F\x0338"+ ,"NotSquareSubsetEqual;" * "\x22E2"+ ,"NotSquareSuperset;" * "\x2290\x0338"+ ,"NotSquareSupersetEqual;" * "\x22E3"+ ,"NotSubset;" * "\x2282\x20D2"+ ,"NotSubsetEqual;" * "\x2288"+ ,"NotSucceeds;" * "\x2281"+ ,"NotSucceedsEqual;" * "\x2AB0\x0338"+ ,"NotSucceedsSlantEqual;" * "\x22E1"+ ,"NotSucceedsTilde;" * "\x227F\x0338"+ ,"NotSuperset;" * "\x2283\x20D2"+ ,"NotSupersetEqual;" * "\x2289"+ ,"NotTilde;" * "\x2241"+ ,"NotTildeEqual;" * "\x2244"+ ,"NotTildeFullEqual;" * "\x2247"+ ,"NotTildeTilde;" * "\x2249"+ ,"NotVerticalBar;" * "\x2224"+ ,"npar;" * "\x2226"+ ,"nparallel;" * "\x2226"+ ,"nparsl;" * "\x2AFD\x20E5"+ ,"npart;" * "\x2202\x0338"+ ,"npolint;" * "\x2A14"+ ,"npr;" * "\x2280"+ ,"nprcue;" * "\x22E0"+ ,"npre;" * "\x2AAF\x0338"+ ,"nprec;" * "\x2280"+ ,"npreceq;" * "\x2AAF\x0338"+ ,"nrarr;" * "\x219B"+ ,"nrArr;" * "\x21CF"+ ,"nrarrc;" * "\x2933\x0338"+ ,"nrarrw;" * "\x219D\x0338"+ ,"nrightarrow;" * "\x219B"+ ,"nRightarrow;" * "\x21CF"+ ,"nrtri;" * "\x22EB"+ ,"nrtrie;" * "\x22ED"+ ,"nsc;" * "\x2281"+ ,"nsccue;" * "\x22E1"+ ,"nsce;" * "\x2AB0\x0338"+ ,"Nscr;" * "\x1D4A9"+ ,"nscr;" * "\x1D4C3"+ ,"nshortmid;" * "\x2224"+ ,"nshortparallel;" * "\x2226"+ ,"nsim;" * "\x2241"+ ,"nsime;" * "\x2244"+ ,"nsimeq;" * "\x2244"+ ,"nsmid;" * "\x2224"+ ,"nspar;" * "\x2226"+ ,"nsqsube;" * "\x22E2"+ ,"nsqsupe;" * "\x22E3"+ ,"nsub;" * "\x2284"+ ,"nsube;" * "\x2288"+ ,"nsubE;" * "\x2AC5\x0338"+ ,"nsubset;" * "\x2282\x20D2"+ ,"nsubseteq;" * "\x2288"+ ,"nsubseteqq;" * "\x2AC5\x0338"+ ,"nsucc;" * "\x2281"+ ,"nsucceq;" * "\x2AB0\x0338"+ ,"nsup;" * "\x2285"+ ,"nsupe;" * "\x2289"+ ,"nsupE;" * "\x2AC6\x0338"+ ,"nsupset;" * "\x2283\x20D2"+ ,"nsupseteq;" * "\x2289"+ ,"nsupseteqq;" * "\x2AC6\x0338"+ ,"ntgl;" * "\x2279"+ ,"Ntilde" * "\x00D1"+ ,"ntilde" * "\x00F1"+ ,"Ntilde;" * "\x00D1"+ ,"ntilde;" * "\x00F1"+ ,"ntlg;" * "\x2278"+ ,"ntriangleleft;" * "\x22EA"+ ,"ntrianglelefteq;" * "\x22EC"+ ,"ntriangleright;" * "\x22EB"+ ,"ntrianglerighteq;" * "\x22ED"+ ,"Nu;" * "\x039D"+ ,"nu;" * "\x03BD"+ ,"num;" * "\x0023"+ ,"numero;" * "\x2116"+ ,"numsp;" * "\x2007"+ ,"nvap;" * "\x224D\x20D2"+ ,"nvdash;" * "\x22AC"+ ,"nvDash;" * "\x22AD"+ ,"nVdash;" * "\x22AE"+ ,"nVDash;" * "\x22AF"+ ,"nvge;" * "\x2265\x20D2"+ ,"nvgt;" * "\x003E\x20D2"+ ,"nvHarr;" * "\x2904"+ ,"nvinfin;" * "\x29DE"+ ,"nvlArr;" * "\x2902"+ ,"nvle;" * "\x2264\x20D2"+ ,"nvlt;" * "\x003C\x20D2"+ ,"nvltrie;" * "\x22B4\x20D2"+ ,"nvrArr;" * "\x2903"+ ,"nvrtrie;" * "\x22B5\x20D2"+ ,"nvsim;" * "\x223C\x20D2"+ ,"nwarhk;" * "\x2923"+ ,"nwarr;" * "\x2196"+ ,"nwArr;" * "\x21D6"+ ,"nwarrow;" * "\x2196"+ ,"nwnear;" * "\x2927"+ ,"Oacute" * "\x00D3"+ ,"oacute" * "\x00F3"+ ,"Oacute;" * "\x00D3"+ ,"oacute;" * "\x00F3"+ ,"oast;" * "\x229B"+ ,"ocir;" * "\x229A"+ ,"Ocirc" * "\x00D4"+ ,"ocirc" * "\x00F4"+ ,"Ocirc;" * "\x00D4"+ ,"ocirc;" * "\x00F4"+ ,"Ocy;" * "\x041E"+ ,"ocy;" * "\x043E"+ ,"odash;" * "\x229D"+ ,"Odblac;" * "\x0150"+ ,"odblac;" * "\x0151"+ ,"odiv;" * "\x2A38"+ ,"odot;" * "\x2299"+ ,"odsold;" * "\x29BC"+ ,"OElig;" * "\x0152"+ ,"oelig;" * "\x0153"+ ,"ofcir;" * "\x29BF"+ ,"Ofr;" * "\x1D512"+ ,"ofr;" * "\x1D52C"+ ,"ogon;" * "\x02DB"+ ,"Ograve" * "\x00D2"+ ,"ograve" * "\x00F2"+ ,"Ograve;" * "\x00D2"+ ,"ograve;" * "\x00F2"+ ,"ogt;" * "\x29C1"+ ,"ohbar;" * "\x29B5"+ ,"ohm;" * "\x03A9"+ ,"oint;" * "\x222E"+ ,"olarr;" * "\x21BA"+ ,"olcir;" * "\x29BE"+ ,"olcross;" * "\x29BB"+ ,"oline;" * "\x203E"+ ,"olt;" * "\x29C0"+ ,"Omacr;" * "\x014C"+ ,"omacr;" * "\x014D"+ ,"Omega;" * "\x03A9"+ ,"omega;" * "\x03C9"+ ,"Omicron;" * "\x039F"+ ,"omicron;" * "\x03BF"+ ,"omid;" * "\x29B6"+ ,"ominus;" * "\x2296"+ ,"Oopf;" * "\x1D546"+ ,"oopf;" * "\x1D560"+ ,"opar;" * "\x29B7"+ ,"OpenCurlyDoubleQuote;" * "\x201C"+ ,"OpenCurlyQuote;" * "\x2018"+ ,"operp;" * "\x29B9"+ ,"oplus;" * "\x2295"+ ,"or;" * "\x2228"+ ,"Or;" * "\x2A54"+ ,"orarr;" * "\x21BB"+ ,"ord;" * "\x2A5D"+ ,"order;" * "\x2134"+ ,"orderof;" * "\x2134"+ ,"ordf" * "\x00AA"+ ,"ordf;" * "\x00AA"+ ,"ordm" * "\x00BA"+ ,"ordm;" * "\x00BA"+ ,"origof;" * "\x22B6"+ ,"oror;" * "\x2A56"+ ,"orslope;" * "\x2A57"+ ,"orv;" * "\x2A5B"+ ,"oS;" * "\x24C8"+ ,"oscr;" * "\x2134"+ ,"Oscr;" * "\x1D4AA"+ ,"Oslash" * "\x00D8"+ ,"oslash" * "\x00F8"+ ,"Oslash;" * "\x00D8"+ ,"oslash;" * "\x00F8"+ ,"osol;" * "\x2298"+ ,"Otilde" * "\x00D5"+ ,"otilde" * "\x00F5"+ ,"Otilde;" * "\x00D5"+ ,"otilde;" * "\x00F5"+ ,"otimes;" * "\x2297"+ ,"Otimes;" * "\x2A37"+ ,"otimesas;" * "\x2A36"+ ,"Ouml" * "\x00D6"+ ,"ouml" * "\x00F6"+ ,"Ouml;" * "\x00D6"+ ,"ouml;" * "\x00F6"+ ,"ovbar;" * "\x233D"+ ,"OverBar;" * "\x203E"+ ,"OverBrace;" * "\x23DE"+ ,"OverBracket;" * "\x23B4"+ ,"OverParenthesis;" * "\x23DC"+ ,"par;" * "\x2225"+ ,"para" * "\x00B6"+ ,"para;" * "\x00B6"+ ,"parallel;" * "\x2225"+ ,"parsim;" * "\x2AF3"+ ,"parsl;" * "\x2AFD"+ ,"part;" * "\x2202"+ ,"PartialD;" * "\x2202"+ ,"Pcy;" * "\x041F"+ ,"pcy;" * "\x043F"+ ,"percnt;" * "\x0025"+ ,"period;" * "\x002E"+ ,"permil;" * "\x2030"+ ,"perp;" * "\x22A5"+ ,"pertenk;" * "\x2031"+ ,"Pfr;" * "\x1D513"+ ,"pfr;" * "\x1D52D"+ ,"Phi;" * "\x03A6"+ ,"phi;" * "\x03C6"+ ,"phiv;" * "\x03D5"+ ,"phmmat;" * "\x2133"+ ,"phone;" * "\x260E"+ ,"Pi;" * "\x03A0"+ ,"pi;" * "\x03C0"+ ,"pitchfork;" * "\x22D4"+ ,"piv;" * "\x03D6"+ ,"planck;" * "\x210F"+ ,"planckh;" * "\x210E"+ ,"plankv;" * "\x210F"+ ,"plus;" * "\x002B"+ ,"plusacir;" * "\x2A23"+ ,"plusb;" * "\x229E"+ ,"pluscir;" * "\x2A22"+ ,"plusdo;" * "\x2214"+ ,"plusdu;" * "\x2A25"+ ,"pluse;" * "\x2A72"+ ,"PlusMinus;" * "\x00B1"+ ,"plusmn" * "\x00B1"+ ,"plusmn;" * "\x00B1"+ ,"plussim;" * "\x2A26"+ ,"plustwo;" * "\x2A27"+ ,"pm;" * "\x00B1"+ ,"Poincareplane;" * "\x210C"+ ,"pointint;" * "\x2A15"+ ,"Popf;" * "\x2119"+ ,"popf;" * "\x1D561"+ ,"pound" * "\x00A3"+ ,"pound;" * "\x00A3"+ ,"pr;" * "\x227A"+ ,"Pr;" * "\x2ABB"+ ,"prap;" * "\x2AB7"+ ,"prcue;" * "\x227C"+ ,"pre;" * "\x2AAF"+ ,"prE;" * "\x2AB3"+ ,"prec;" * "\x227A"+ ,"precapprox;" * "\x2AB7"+ ,"preccurlyeq;" * "\x227C"+ ,"Precedes;" * "\x227A"+ ,"PrecedesEqual;" * "\x2AAF"+ ,"PrecedesSlantEqual;" * "\x227C"+ ,"PrecedesTilde;" * "\x227E"+ ,"preceq;" * "\x2AAF"+ ,"precnapprox;" * "\x2AB9"+ ,"precneqq;" * "\x2AB5"+ ,"precnsim;" * "\x22E8"+ ,"precsim;" * "\x227E"+ ,"prime;" * "\x2032"+ ,"Prime;" * "\x2033"+ ,"primes;" * "\x2119"+ ,"prnap;" * "\x2AB9"+ ,"prnE;" * "\x2AB5"+ ,"prnsim;" * "\x22E8"+ ,"prod;" * "\x220F"+ ,"Product;" * "\x220F"+ ,"profalar;" * "\x232E"+ ,"profline;" * "\x2312"+ ,"profsurf;" * "\x2313"+ ,"prop;" * "\x221D"+ ,"Proportion;" * "\x2237"+ ,"Proportional;" * "\x221D"+ ,"propto;" * "\x221D"+ ,"prsim;" * "\x227E"+ ,"prurel;" * "\x22B0"+ ,"Pscr;" * "\x1D4AB"+ ,"pscr;" * "\x1D4C5"+ ,"Psi;" * "\x03A8"+ ,"psi;" * "\x03C8"+ ,"puncsp;" * "\x2008"+ ,"Qfr;" * "\x1D514"+ ,"qfr;" * "\x1D52E"+ ,"qint;" * "\x2A0C"+ ,"Qopf;" * "\x211A"+ ,"qopf;" * "\x1D562"+ ,"qprime;" * "\x2057"+ ,"Qscr;" * "\x1D4AC"+ ,"qscr;" * "\x1D4C6"+ ,"quaternions;" * "\x210D"+ ,"quatint;" * "\x2A16"+ ,"quest;" * "\x003F"+ ,"questeq;" * "\x225F"+ ,"QUOT" * "\x0022"+ ,"quot" * "\x0022"+ ,"QUOT;" * "\x0022"+ ,"quot;" * "\x0022"+ ,"rAarr;" * "\x21DB"+ ,"race;" * "\x223D\x0331"+ ,"Racute;" * "\x0154"+ ,"racute;" * "\x0155"+ ,"radic;" * "\x221A"+ ,"raemptyv;" * "\x29B3"+ ,"rang;" * "\x27E9"+ ,"Rang;" * "\x27EB"+ ,"rangd;" * "\x2992"+ ,"range;" * "\x29A5"+ ,"rangle;" * "\x27E9"+ ,"raquo" * "\x00BB"+ ,"raquo;" * "\x00BB"+ ,"rarr;" * "\x2192"+ ,"Rarr;" * "\x21A0"+ ,"rArr;" * "\x21D2"+ ,"rarrap;" * "\x2975"+ ,"rarrb;" * "\x21E5"+ ,"rarrbfs;" * "\x2920"+ ,"rarrc;" * "\x2933"+ ,"rarrfs;" * "\x291E"+ ,"rarrhk;" * "\x21AA"+ ,"rarrlp;" * "\x21AC"+ ,"rarrpl;" * "\x2945"+ ,"rarrsim;" * "\x2974"+ ,"rarrtl;" * "\x21A3"+ ,"Rarrtl;" * "\x2916"+ ,"rarrw;" * "\x219D"+ ,"ratail;" * "\x291A"+ ,"rAtail;" * "\x291C"+ ,"ratio;" * "\x2236"+ ,"rationals;" * "\x211A"+ ,"rbarr;" * "\x290D"+ ,"rBarr;" * "\x290F"+ ,"RBarr;" * "\x2910"+ ,"rbbrk;" * "\x2773"+ ,"rbrace;" * "\x007D"+ ,"rbrack;" * "\x005D"+ ,"rbrke;" * "\x298C"+ ,"rbrksld;" * "\x298E"+ ,"rbrkslu;" * "\x2990"+ ,"Rcaron;" * "\x0158"+ ,"rcaron;" * "\x0159"+ ,"Rcedil;" * "\x0156"+ ,"rcedil;" * "\x0157"+ ,"rceil;" * "\x2309"+ ,"rcub;" * "\x007D"+ ,"Rcy;" * "\x0420"+ ,"rcy;" * "\x0440"+ ,"rdca;" * "\x2937"+ ,"rdldhar;" * "\x2969"+ ,"rdquo;" * "\x201D"+ ,"rdquor;" * "\x201D"+ ,"rdsh;" * "\x21B3"+ ,"Re;" * "\x211C"+ ,"real;" * "\x211C"+ ,"realine;" * "\x211B"+ ,"realpart;" * "\x211C"+ ,"reals;" * "\x211D"+ ,"rect;" * "\x25AD"+ ,"REG" * "\x00AE"+ ,"reg" * "\x00AE"+ ,"REG;" * "\x00AE"+ ,"reg;" * "\x00AE"+ ,"ReverseElement;" * "\x220B"+ ,"ReverseEquilibrium;" * "\x21CB"+ ,"ReverseUpEquilibrium;" * "\x296F"+ ,"rfisht;" * "\x297D"+ ,"rfloor;" * "\x230B"+ ,"Rfr;" * "\x211C"+ ,"rfr;" * "\x1D52F"+ ,"rHar;" * "\x2964"+ ,"rhard;" * "\x21C1"+ ,"rharu;" * "\x21C0"+ ,"rharul;" * "\x296C"+ ,"Rho;" * "\x03A1"+ ,"rho;" * "\x03C1"+ ,"rhov;" * "\x03F1"+ ,"RightAngleBracket;" * "\x27E9"+ ,"RightArrow;" * "\x2192"+ ,"rightarrow;" * "\x2192"+ ,"Rightarrow;" * "\x21D2"+ ,"RightArrowBar;" * "\x21E5"+ ,"RightArrowLeftArrow;" * "\x21C4"+ ,"rightarrowtail;" * "\x21A3"+ ,"RightCeiling;" * "\x2309"+ ,"RightDoubleBracket;" * "\x27E7"+ ,"RightDownTeeVector;" * "\x295D"+ ,"RightDownVector;" * "\x21C2"+ ,"RightDownVectorBar;" * "\x2955"+ ,"RightFloor;" * "\x230B"+ ,"rightharpoondown;" * "\x21C1"+ ,"rightharpoonup;" * "\x21C0"+ ,"rightleftarrows;" * "\x21C4"+ ,"rightleftharpoons;" * "\x21CC"+ ,"rightrightarrows;" * "\x21C9"+ ,"rightsquigarrow;" * "\x219D"+ ,"RightTee;" * "\x22A2"+ ,"RightTeeArrow;" * "\x21A6"+ ,"RightTeeVector;" * "\x295B"+ ,"rightthreetimes;" * "\x22CC"+ ,"RightTriangle;" * "\x22B3"+ ,"RightTriangleBar;" * "\x29D0"+ ,"RightTriangleEqual;" * "\x22B5"+ ,"RightUpDownVector;" * "\x294F"+ ,"RightUpTeeVector;" * "\x295C"+ ,"RightUpVector;" * "\x21BE"+ ,"RightUpVectorBar;" * "\x2954"+ ,"RightVector;" * "\x21C0"+ ,"RightVectorBar;" * "\x2953"+ ,"ring;" * "\x02DA"+ ,"risingdotseq;" * "\x2253"+ ,"rlarr;" * "\x21C4"+ ,"rlhar;" * "\x21CC"+ ,"rlm;" * "\x200F"+ ,"rmoust;" * "\x23B1"+ ,"rmoustache;" * "\x23B1"+ ,"rnmid;" * "\x2AEE"+ ,"roang;" * "\x27ED"+ ,"roarr;" * "\x21FE"+ ,"robrk;" * "\x27E7"+ ,"ropar;" * "\x2986"+ ,"Ropf;" * "\x211D"+ ,"ropf;" * "\x1D563"+ ,"roplus;" * "\x2A2E"+ ,"rotimes;" * "\x2A35"+ ,"RoundImplies;" * "\x2970"+ ,"rpar;" * "\x0029"+ ,"rpargt;" * "\x2994"+ ,"rppolint;" * "\x2A12"+ ,"rrarr;" * "\x21C9"+ ,"Rrightarrow;" * "\x21DB"+ ,"rsaquo;" * "\x203A"+ ,"Rscr;" * "\x211B"+ ,"rscr;" * "\x1D4C7"+ ,"Rsh;" * "\x21B1"+ ,"rsh;" * "\x21B1"+ ,"rsqb;" * "\x005D"+ ,"rsquo;" * "\x2019"+ ,"rsquor;" * "\x2019"+ ,"rthree;" * "\x22CC"+ ,"rtimes;" * "\x22CA"+ ,"rtri;" * "\x25B9"+ ,"rtrie;" * "\x22B5"+ ,"rtrif;" * "\x25B8"+ ,"rtriltri;" * "\x29CE"+ ,"RuleDelayed;" * "\x29F4"+ ,"ruluhar;" * "\x2968"+ ,"rx;" * "\x211E"+ ,"Sacute;" * "\x015A"+ ,"sacute;" * "\x015B"+ ,"sbquo;" * "\x201A"+ ,"sc;" * "\x227B"+ ,"Sc;" * "\x2ABC"+ ,"scap;" * "\x2AB8"+ ,"Scaron;" * "\x0160"+ ,"scaron;" * "\x0161"+ ,"sccue;" * "\x227D"+ ,"sce;" * "\x2AB0"+ ,"scE;" * "\x2AB4"+ ,"Scedil;" * "\x015E"+ ,"scedil;" * "\x015F"+ ,"Scirc;" * "\x015C"+ ,"scirc;" * "\x015D"+ ,"scnap;" * "\x2ABA"+ ,"scnE;" * "\x2AB6"+ ,"scnsim;" * "\x22E9"+ ,"scpolint;" * "\x2A13"+ ,"scsim;" * "\x227F"+ ,"Scy;" * "\x0421"+ ,"scy;" * "\x0441"+ ,"sdot;" * "\x22C5"+ ,"sdotb;" * "\x22A1"+ ,"sdote;" * "\x2A66"+ ,"searhk;" * "\x2925"+ ,"searr;" * "\x2198"+ ,"seArr;" * "\x21D8"+ ,"searrow;" * "\x2198"+ ,"sect" * "\x00A7"+ ,"sect;" * "\x00A7"+ ,"semi;" * "\x003B"+ ,"seswar;" * "\x2929"+ ,"setminus;" * "\x2216"+ ,"setmn;" * "\x2216"+ ,"sext;" * "\x2736"+ ,"Sfr;" * "\x1D516"+ ,"sfr;" * "\x1D530"+ ,"sfrown;" * "\x2322"+ ,"sharp;" * "\x266F"+ ,"SHCHcy;" * "\x0429"+ ,"shchcy;" * "\x0449"+ ,"SHcy;" * "\x0428"+ ,"shcy;" * "\x0448"+ ,"ShortDownArrow;" * "\x2193"+ ,"ShortLeftArrow;" * "\x2190"+ ,"shortmid;" * "\x2223"+ ,"shortparallel;" * "\x2225"+ ,"ShortRightArrow;" * "\x2192"+ ,"ShortUpArrow;" * "\x2191"+ ,"shy" * "\x00AD"+ ,"shy;" * "\x00AD"+ ,"Sigma;" * "\x03A3"+ ,"sigma;" * "\x03C3"+ ,"sigmaf;" * "\x03C2"+ ,"sigmav;" * "\x03C2"+ ,"sim;" * "\x223C"+ ,"simdot;" * "\x2A6A"+ ,"sime;" * "\x2243"+ ,"simeq;" * "\x2243"+ ,"simg;" * "\x2A9E"+ ,"simgE;" * "\x2AA0"+ ,"siml;" * "\x2A9D"+ ,"simlE;" * "\x2A9F"+ ,"simne;" * "\x2246"+ ,"simplus;" * "\x2A24"+ ,"simrarr;" * "\x2972"+ ,"slarr;" * "\x2190"+ ,"SmallCircle;" * "\x2218"+ ,"smallsetminus;" * "\x2216"+ ,"smashp;" * "\x2A33"+ ,"smeparsl;" * "\x29E4"+ ,"smid;" * "\x2223"+ ,"smile;" * "\x2323"+ ,"smt;" * "\x2AAA"+ ,"smte;" * "\x2AAC"+ ,"smtes;" * "\x2AAC\xFE00"+ ,"SOFTcy;" * "\x042C"+ ,"softcy;" * "\x044C"+ ,"sol;" * "\x002F"+ ,"solb;" * "\x29C4"+ ,"solbar;" * "\x233F"+ ,"Sopf;" * "\x1D54A"+ ,"sopf;" * "\x1D564"+ ,"spades;" * "\x2660"+ ,"spadesuit;" * "\x2660"+ ,"spar;" * "\x2225"+ ,"sqcap;" * "\x2293"+ ,"sqcaps;" * "\x2293\xFE00"+ ,"sqcup;" * "\x2294"+ ,"sqcups;" * "\x2294\xFE00"+ ,"Sqrt;" * "\x221A"+ ,"sqsub;" * "\x228F"+ ,"sqsube;" * "\x2291"+ ,"sqsubset;" * "\x228F"+ ,"sqsubseteq;" * "\x2291"+ ,"sqsup;" * "\x2290"+ ,"sqsupe;" * "\x2292"+ ,"sqsupset;" * "\x2290"+ ,"sqsupseteq;" * "\x2292"+ ,"squ;" * "\x25A1"+ ,"Square;" * "\x25A1"+ ,"square;" * "\x25A1"+ ,"SquareIntersection;" * "\x2293"+ ,"SquareSubset;" * "\x228F"+ ,"SquareSubsetEqual;" * "\x2291"+ ,"SquareSuperset;" * "\x2290"+ ,"SquareSupersetEqual;" * "\x2292"+ ,"SquareUnion;" * "\x2294"+ ,"squarf;" * "\x25AA"+ ,"squf;" * "\x25AA"+ ,"srarr;" * "\x2192"+ ,"Sscr;" * "\x1D4AE"+ ,"sscr;" * "\x1D4C8"+ ,"ssetmn;" * "\x2216"+ ,"ssmile;" * "\x2323"+ ,"sstarf;" * "\x22C6"+ ,"Star;" * "\x22C6"+ ,"star;" * "\x2606"+ ,"starf;" * "\x2605"+ ,"straightepsilon;" * "\x03F5"+ ,"straightphi;" * "\x03D5"+ ,"strns;" * "\x00AF"+ ,"sub;" * "\x2282"+ ,"Sub;" * "\x22D0"+ ,"subdot;" * "\x2ABD"+ ,"sube;" * "\x2286"+ ,"subE;" * "\x2AC5"+ ,"subedot;" * "\x2AC3"+ ,"submult;" * "\x2AC1"+ ,"subne;" * "\x228A"+ ,"subnE;" * "\x2ACB"+ ,"subplus;" * "\x2ABF"+ ,"subrarr;" * "\x2979"+ ,"subset;" * "\x2282"+ ,"Subset;" * "\x22D0"+ ,"subseteq;" * "\x2286"+ ,"subseteqq;" * "\x2AC5"+ ,"SubsetEqual;" * "\x2286"+ ,"subsetneq;" * "\x228A"+ ,"subsetneqq;" * "\x2ACB"+ ,"subsim;" * "\x2AC7"+ ,"subsub;" * "\x2AD5"+ ,"subsup;" * "\x2AD3"+ ,"succ;" * "\x227B"+ ,"succapprox;" * "\x2AB8"+ ,"succcurlyeq;" * "\x227D"+ ,"Succeeds;" * "\x227B"+ ,"SucceedsEqual;" * "\x2AB0"+ ,"SucceedsSlantEqual;" * "\x227D"+ ,"SucceedsTilde;" * "\x227F"+ ,"succeq;" * "\x2AB0"+ ,"succnapprox;" * "\x2ABA"+ ,"succneqq;" * "\x2AB6"+ ,"succnsim;" * "\x22E9"+ ,"succsim;" * "\x227F"+ ,"SuchThat;" * "\x220B"+ ,"Sum;" * "\x2211"+ ,"sum;" * "\x2211"+ ,"sung;" * "\x266A"+ ,"sup1" * "\x00B9"+ ,"sup1;" * "\x00B9"+ ,"sup2" * "\x00B2"+ ,"sup2;" * "\x00B2"+ ,"sup3" * "\x00B3"+ ,"sup3;" * "\x00B3"+ ,"sup;" * "\x2283"+ ,"Sup;" * "\x22D1"+ ,"supdot;" * "\x2ABE"+ ,"supdsub;" * "\x2AD8"+ ,"supe;" * "\x2287"+ ,"supE;" * "\x2AC6"+ ,"supedot;" * "\x2AC4"+ ,"Superset;" * "\x2283"+ ,"SupersetEqual;" * "\x2287"+ ,"suphsol;" * "\x27C9"+ ,"suphsub;" * "\x2AD7"+ ,"suplarr;" * "\x297B"+ ,"supmult;" * "\x2AC2"+ ,"supne;" * "\x228B"+ ,"supnE;" * "\x2ACC"+ ,"supplus;" * "\x2AC0"+ ,"supset;" * "\x2283"+ ,"Supset;" * "\x22D1"+ ,"supseteq;" * "\x2287"+ ,"supseteqq;" * "\x2AC6"+ ,"supsetneq;" * "\x228B"+ ,"supsetneqq;" * "\x2ACC"+ ,"supsim;" * "\x2AC8"+ ,"supsub;" * "\x2AD4"+ ,"supsup;" * "\x2AD6"+ ,"swarhk;" * "\x2926"+ ,"swarr;" * "\x2199"+ ,"swArr;" * "\x21D9"+ ,"swarrow;" * "\x2199"+ ,"swnwar;" * "\x292A"+ ,"szlig" * "\x00DF"+ ,"szlig;" * "\x00DF"+ ,"Tab;" * "\x0009"+ ,"target;" * "\x2316"+ ,"Tau;" * "\x03A4"+ ,"tau;" * "\x03C4"+ ,"tbrk;" * "\x23B4"+ ,"Tcaron;" * "\x0164"+ ,"tcaron;" * "\x0165"+ ,"Tcedil;" * "\x0162"+ ,"tcedil;" * "\x0163"+ ,"Tcy;" * "\x0422"+ ,"tcy;" * "\x0442"+ ,"tdot;" * "\x20DB"+ ,"telrec;" * "\x2315"+ ,"Tfr;" * "\x1D517"+ ,"tfr;" * "\x1D531"+ ,"there4;" * "\x2234"+ ,"Therefore;" * "\x2234"+ ,"therefore;" * "\x2234"+ ,"Theta;" * "\x0398"+ ,"theta;" * "\x03B8"+ ,"thetasym;" * "\x03D1"+ ,"thetav;" * "\x03D1"+ ,"thickapprox;" * "\x2248"+ ,"thicksim;" * "\x223C"+ ,"ThickSpace;" * "\x205F\x200A"+ ,"thinsp;" * "\x2009"+ ,"ThinSpace;" * "\x2009"+ ,"thkap;" * "\x2248"+ ,"thksim;" * "\x223C"+ ,"THORN" * "\x00DE"+ ,"thorn" * "\x00FE"+ ,"THORN;" * "\x00DE"+ ,"thorn;" * "\x00FE"+ ,"tilde;" * "\x02DC"+ ,"Tilde;" * "\x223C"+ ,"TildeEqual;" * "\x2243"+ ,"TildeFullEqual;" * "\x2245"+ ,"TildeTilde;" * "\x2248"+ ,"times" * "\x00D7"+ ,"times;" * "\x00D7"+ ,"timesb;" * "\x22A0"+ ,"timesbar;" * "\x2A31"+ ,"timesd;" * "\x2A30"+ ,"tint;" * "\x222D"+ ,"toea;" * "\x2928"+ ,"top;" * "\x22A4"+ ,"topbot;" * "\x2336"+ ,"topcir;" * "\x2AF1"+ ,"Topf;" * "\x1D54B"+ ,"topf;" * "\x1D565"+ ,"topfork;" * "\x2ADA"+ ,"tosa;" * "\x2929"+ ,"tprime;" * "\x2034"+ ,"TRADE;" * "\x2122"+ ,"trade;" * "\x2122"+ ,"triangle;" * "\x25B5"+ ,"triangledown;" * "\x25BF"+ ,"triangleleft;" * "\x25C3"+ ,"trianglelefteq;" * "\x22B4"+ ,"triangleq;" * "\x225C"+ ,"triangleright;" * "\x25B9"+ ,"trianglerighteq;" * "\x22B5"+ ,"tridot;" * "\x25EC"+ ,"trie;" * "\x225C"+ ,"triminus;" * "\x2A3A"+ ,"TripleDot;" * "\x20DB"+ ,"triplus;" * "\x2A39"+ ,"trisb;" * "\x29CD"+ ,"tritime;" * "\x2A3B"+ ,"trpezium;" * "\x23E2"+ ,"Tscr;" * "\x1D4AF"+ ,"tscr;" * "\x1D4C9"+ ,"TScy;" * "\x0426"+ ,"tscy;" * "\x0446"+ ,"TSHcy;" * "\x040B"+ ,"tshcy;" * "\x045B"+ ,"Tstrok;" * "\x0166"+ ,"tstrok;" * "\x0167"+ ,"twixt;" * "\x226C"+ ,"twoheadleftarrow;" * "\x219E"+ ,"twoheadrightarrow;" * "\x21A0"+ ,"Uacute" * "\x00DA"+ ,"uacute" * "\x00FA"+ ,"Uacute;" * "\x00DA"+ ,"uacute;" * "\x00FA"+ ,"uarr;" * "\x2191"+ ,"Uarr;" * "\x219F"+ ,"uArr;" * "\x21D1"+ ,"Uarrocir;" * "\x2949"+ ,"Ubrcy;" * "\x040E"+ ,"ubrcy;" * "\x045E"+ ,"Ubreve;" * "\x016C"+ ,"ubreve;" * "\x016D"+ ,"Ucirc" * "\x00DB"+ ,"ucirc" * "\x00FB"+ ,"Ucirc;" * "\x00DB"+ ,"ucirc;" * "\x00FB"+ ,"Ucy;" * "\x0423"+ ,"ucy;" * "\x0443"+ ,"udarr;" * "\x21C5"+ ,"Udblac;" * "\x0170"+ ,"udblac;" * "\x0171"+ ,"udhar;" * "\x296E"+ ,"ufisht;" * "\x297E"+ ,"Ufr;" * "\x1D518"+ ,"ufr;" * "\x1D532"+ ,"Ugrave" * "\x00D9"+ ,"ugrave" * "\x00F9"+ ,"Ugrave;" * "\x00D9"+ ,"ugrave;" * "\x00F9"+ ,"uHar;" * "\x2963"+ ,"uharl;" * "\x21BF"+ ,"uharr;" * "\x21BE"+ ,"uhblk;" * "\x2580"+ ,"ulcorn;" * "\x231C"+ ,"ulcorner;" * "\x231C"+ ,"ulcrop;" * "\x230F"+ ,"ultri;" * "\x25F8"+ ,"Umacr;" * "\x016A"+ ,"umacr;" * "\x016B"+ ,"uml" * "\x00A8"+ ,"uml;" * "\x00A8"+ ,"UnderBar;" * "\x005F"+ ,"UnderBrace;" * "\x23DF"+ ,"UnderBracket;" * "\x23B5"+ ,"UnderParenthesis;" * "\x23DD"+ ,"Union;" * "\x22C3"+ ,"UnionPlus;" * "\x228E"+ ,"Uogon;" * "\x0172"+ ,"uogon;" * "\x0173"+ ,"Uopf;" * "\x1D54C"+ ,"uopf;" * "\x1D566"+ ,"UpArrow;" * "\x2191"+ ,"uparrow;" * "\x2191"+ ,"Uparrow;" * "\x21D1"+ ,"UpArrowBar;" * "\x2912"+ ,"UpArrowDownArrow;" * "\x21C5"+ ,"UpDownArrow;" * "\x2195"+ ,"updownarrow;" * "\x2195"+ ,"Updownarrow;" * "\x21D5"+ ,"UpEquilibrium;" * "\x296E"+ ,"upharpoonleft;" * "\x21BF"+ ,"upharpoonright;" * "\x21BE"+ ,"uplus;" * "\x228E"+ ,"UpperLeftArrow;" * "\x2196"+ ,"UpperRightArrow;" * "\x2197"+ ,"upsi;" * "\x03C5"+ ,"Upsi;" * "\x03D2"+ ,"upsih;" * "\x03D2"+ ,"Upsilon;" * "\x03A5"+ ,"upsilon;" * "\x03C5"+ ,"UpTee;" * "\x22A5"+ ,"UpTeeArrow;" * "\x21A5"+ ,"upuparrows;" * "\x21C8"+ ,"urcorn;" * "\x231D"+ ,"urcorner;" * "\x231D"+ ,"urcrop;" * "\x230E"+ ,"Uring;" * "\x016E"+ ,"uring;" * "\x016F"+ ,"urtri;" * "\x25F9"+ ,"Uscr;" * "\x1D4B0"+ ,"uscr;" * "\x1D4CA"+ ,"utdot;" * "\x22F0"+ ,"Utilde;" * "\x0168"+ ,"utilde;" * "\x0169"+ ,"utri;" * "\x25B5"+ ,"utrif;" * "\x25B4"+ ,"uuarr;" * "\x21C8"+ ,"Uuml" * "\x00DC"+ ,"uuml" * "\x00FC"+ ,"Uuml;" * "\x00DC"+ ,"uuml;" * "\x00FC"+ ,"uwangle;" * "\x29A7"+ ,"vangrt;" * "\x299C"+ ,"varepsilon;" * "\x03F5"+ ,"varkappa;" * "\x03F0"+ ,"varnothing;" * "\x2205"+ ,"varphi;" * "\x03D5"+ ,"varpi;" * "\x03D6"+ ,"varpropto;" * "\x221D"+ ,"varr;" * "\x2195"+ ,"vArr;" * "\x21D5"+ ,"varrho;" * "\x03F1"+ ,"varsigma;" * "\x03C2"+ ,"varsubsetneq;" * "\x228A\xFE00"+ ,"varsubsetneqq;" * "\x2ACB\xFE00"+ ,"varsupsetneq;" * "\x228B\xFE00"+ ,"varsupsetneqq;" * "\x2ACC\xFE00"+ ,"vartheta;" * "\x03D1"+ ,"vartriangleleft;" * "\x22B2"+ ,"vartriangleright;" * "\x22B3"+ ,"vBar;" * "\x2AE8"+ ,"Vbar;" * "\x2AEB"+ ,"vBarv;" * "\x2AE9"+ ,"Vcy;" * "\x0412"+ ,"vcy;" * "\x0432"+ ,"vdash;" * "\x22A2"+ ,"vDash;" * "\x22A8"+ ,"Vdash;" * "\x22A9"+ ,"VDash;" * "\x22AB"+ ,"Vdashl;" * "\x2AE6"+ ,"vee;" * "\x2228"+ ,"Vee;" * "\x22C1"+ ,"veebar;" * "\x22BB"+ ,"veeeq;" * "\x225A"+ ,"vellip;" * "\x22EE"+ ,"verbar;" * "\x007C"+ ,"Verbar;" * "\x2016"+ ,"vert;" * "\x007C"+ ,"Vert;" * "\x2016"+ ,"VerticalBar;" * "\x2223"+ ,"VerticalLine;" * "\x007C"+ ,"VerticalSeparator;" * "\x2758"+ ,"VerticalTilde;" * "\x2240"+ ,"VeryThinSpace;" * "\x200A"+ ,"Vfr;" * "\x1D519"+ ,"vfr;" * "\x1D533"+ ,"vltri;" * "\x22B2"+ ,"vnsub;" * "\x2282\x20D2"+ ,"vnsup;" * "\x2283\x20D2"+ ,"Vopf;" * "\x1D54D"+ ,"vopf;" * "\x1D567"+ ,"vprop;" * "\x221D"+ ,"vrtri;" * "\x22B3"+ ,"Vscr;" * "\x1D4B1"+ ,"vscr;" * "\x1D4CB"+ ,"vsubne;" * "\x228A\xFE00"+ ,"vsubnE;" * "\x2ACB\xFE00"+ ,"vsupne;" * "\x228B\xFE00"+ ,"vsupnE;" * "\x2ACC\xFE00"+ ,"Vvdash;" * "\x22AA"+ ,"vzigzag;" * "\x299A"+ ,"Wcirc;" * "\x0174"+ ,"wcirc;" * "\x0175"+ ,"wedbar;" * "\x2A5F"+ ,"wedge;" * "\x2227"+ ,"Wedge;" * "\x22C0"+ ,"wedgeq;" * "\x2259"+ ,"weierp;" * "\x2118"+ ,"Wfr;" * "\x1D51A"+ ,"wfr;" * "\x1D534"+ ,"Wopf;" * "\x1D54E"+ ,"wopf;" * "\x1D568"+ ,"wp;" * "\x2118"+ ,"wr;" * "\x2240"+ ,"wreath;" * "\x2240"+ ,"Wscr;" * "\x1D4B2"+ ,"wscr;" * "\x1D4CC"+ ,"xcap;" * "\x22C2"+ ,"xcirc;" * "\x25EF"+ ,"xcup;" * "\x22C3"+ ,"xdtri;" * "\x25BD"+ ,"Xfr;" * "\x1D51B"+ ,"xfr;" * "\x1D535"+ ,"xharr;" * "\x27F7"+ ,"xhArr;" * "\x27FA"+ ,"Xi;" * "\x039E"+ ,"xi;" * "\x03BE"+ ,"xlarr;" * "\x27F5"+ ,"xlArr;" * "\x27F8"+ ,"xmap;" * "\x27FC"+ ,"xnis;" * "\x22FB"+ ,"xodot;" * "\x2A00"+ ,"Xopf;" * "\x1D54F"+ ,"xopf;" * "\x1D569"+ ,"xoplus;" * "\x2A01"+ ,"xotime;" * "\x2A02"+ ,"xrarr;" * "\x27F6"+ ,"xrArr;" * "\x27F9"+ ,"Xscr;" * "\x1D4B3"+ ,"xscr;" * "\x1D4CD"+ ,"xsqcup;" * "\x2A06"+ ,"xuplus;" * "\x2A04"+ ,"xutri;" * "\x25B3"+ ,"xvee;" * "\x22C1"+ ,"xwedge;" * "\x22C0"+ ,"Yacute" * "\x00DD"+ ,"yacute" * "\x00FD"+ ,"Yacute;" * "\x00DD"+ ,"yacute;" * "\x00FD"+ ,"YAcy;" * "\x042F"+ ,"yacy;" * "\x044F"+ ,"Ycirc;" * "\x0176"+ ,"ycirc;" * "\x0177"+ ,"Ycy;" * "\x042B"+ ,"ycy;" * "\x044B"+ ,"yen" * "\x00A5"+ ,"yen;" * "\x00A5"+ ,"Yfr;" * "\x1D51C"+ ,"yfr;" * "\x1D536"+ ,"YIcy;" * "\x0407"+ ,"yicy;" * "\x0457"+ ,"Yopf;" * "\x1D550"+ ,"yopf;" * "\x1D56A"+ ,"Yscr;" * "\x1D4B4"+ ,"yscr;" * "\x1D4CE"+ ,"YUcy;" * "\x042E"+ ,"yucy;" * "\x044E"+ ,"yuml" * "\x00FF"+ ,"yuml;" * "\x00FF"+ ,"Yuml;" * "\x0178"+ ,"Zacute;" * "\x0179"+ ,"zacute;" * "\x017A"+ ,"Zcaron;" * "\x017D"+ ,"zcaron;" * "\x017E"+ ,"Zcy;" * "\x0417"+ ,"zcy;" * "\x0437"+ ,"Zdot;" * "\x017B"+ ,"zdot;" * "\x017C"+ ,"zeetrf;" * "\x2128"+ ,"ZeroWidthSpace;" * "\x200B"+ ,"Zeta;" * "\x0396"+ ,"zeta;" * "\x03B6"+ ,"Zfr;" * "\x2128"+ ,"zfr;" * "\x1D537"+ ,"ZHcy;" * "\x0416"+ ,"zhcy;" * "\x0436"+ ,"zigrarr;" * "\x21DD"+ ,"Zopf;" * "\x2124"+ ,"zopf;" * "\x1D56B"+ ,"Zscr;" * "\x1D4B5"+ ,"zscr;" * "\x1D4CF"+ ,"zwj;" * "\x200D"+ ,"zwnj;" * "\x200C"+ ]
+ src/Text/HTML/TagSoup/Generated.hs view
@@ -0,0 +1,2 @@+module Text.HTML.TagSoup.Generated(parseTagsOptions) where+import Text.HTML.TagSoup.Manual
+ src/Text/HTML/TagSoup/Implementation.hs view
@@ -0,0 +1,193 @@+{-# LANGUAGE RecordWildCards, PatternGuards, ScopedTypeVariables #-}++module Text.HTML.TagSoup.Implementation where++import Text.HTML.TagSoup.Type+import Text.HTML.TagSoup.Options+import Text.StringLike as Str+import Numeric (readHex)+import Data.Char (chr, ord)+import Data.Ix+import Control.Exception(assert)+import Control.Arrow++---------------------------------------------------------------------+-- BOTTOM LAYER++data Out+ = Char Char+ | Tag -- <+ | TagShut -- </+ | AttName+ | AttVal+ | TagEnd -- >+ | TagEndClose -- />+ | Comment -- <!--+ | CommentEnd -- -->+ | EntityName -- &+ | EntityNum -- &#+ | EntityHex -- &#x+ | EntityEnd Bool -- Attributed followed by ; for True, missing ; for False+ | Warn String+ | Pos Position+ deriving (Show,Eq)++errSeen x = Warn $ "Unexpected " ++ show x+errWant x = Warn $ "Expected " ++ show x++data S = S+ {s :: S+ ,tl :: S+ ,hd :: Char+ ,eof :: Bool+ ,next :: String -> Maybe S+ ,pos :: [Out] -> [Out]+ }+++expand :: Position -> String -> S+expand p text = p `seq` res+ where res = S{s = res+ ,tl = expand (positionChar p (head text)) (tail text)+ ,hd = if null text then '\0' else head text+ ,eof = null text+ ,next = next p text+ ,pos = (Pos p:)+ }++ next p (t:ext) (s:tr) | t == s = next (positionChar p t) ext tr+ next p text [] = Just $ expand p text+ next _ _ _ = Nothing+++infixr &++class Outable a where (&) :: a -> [Out] -> [Out]+instance Outable Char where (&) = ampChar+instance Outable Out where (&) = ampOut+ampChar x y = Char x : y+ampOut x y = x : y+++state :: String -> S+state s = expand nullPosition s++---------------------------------------------------------------------+-- TOP LAYER+++output :: forall str . StringLike str => ParseOptions str -> [Out] -> [Tag str]+output ParseOptions{..} x = (if optTagTextMerge then tagTextMerge else id) $ go ((nullPosition,[]),x)+ where+ -- main choice loop+ go :: ((Position,[Tag str]),[Out]) -> [Tag str]+ go ((p,ws),xs) | p `seq` False = [] -- otherwise p is a space leak when optTagPosition == False+ go ((p,ws),xs) | not $ null ws = (if optTagWarning then (reverse ws++) else id) $ go ((p,[]),xs)+ go ((p,ws),Pos p2:xs) = go ((p2,ws),xs)++ go x | isChar x = pos x $ TagText a : go y+ where (y,a) = charsStr x+ go x | isTag x = pos x $ TagOpen a b : (if isTagEndClose z then pos x $ TagClose a : go (next z) else go (skip isTagEnd z))+ where (y,a) = charsStr $ next x+ (z,b) = atts y+ go x | isTagShut x = pos x $ (TagClose a:) $+ (if not (null b) then warn x "Unexpected attributes in close tag" else id) $+ if isTagEndClose z then warn x "Unexpected self-closing in close tag" $ go (next z) else go (skip isTagEnd z)+ where (y,a) = charsStr $ next x+ (z,b) = atts y+ go x | isComment x = pos x $ TagComment a : go (skip isCommentEnd y)+ where (y,a) = charsStr $ next x+ go x | isEntityName x = poss x ((if optTagWarning then id else filter (not . isTagWarning)) $ optEntityData (a, getEntityEnd y)) ++ go (skip isEntityEnd y) + where (y,a) = charsStr $ next x+ go x | isEntityNumHex x = pos x $ TagText (fromChar $ entityChr x a) : go (skip isEntityEnd y)+ where (y,a) = chars $ next x+ go x | Just a <- fromWarn x = if optTagWarning then pos x $ TagWarning (fromString a) : go (next x) else go (next x)+ go x | isEof x = []++ atts :: ((Position,[Tag str]),[Out]) -> ( ((Position,[Tag str]),[Out]) , [(str,str)] )+ atts x | isAttName x = second ((a,b):) $ atts z+ where (y,a) = charsStr (next x)+ (z,b) = if isAttVal y then charsEntsStr (next y) else (y, empty)+ atts x | isAttVal x = second ((empty,a):) $ atts y+ where (y,a) = charsEntsStr (next x)+ atts x = (x, [])++ -- chars+ chars x = charss False x+ charsStr x = (id *** fromString) $ chars x+ charsEntsStr x = (id *** fromString) $ charss True x++ -- loop round collecting characters, if the b is set including entity+ charss :: Bool -> ((Position,[Tag str]),[Out]) -> ( ((Position,[Tag str]),[Out]) , String)+ charss t x | Just a <- fromChr x = (y, a:b)+ where (y,b) = charss t (next x)+ charss t x | t, isEntityName x = second (toString n ++) $ charss t $ addWarns m z+ where (y,a) = charsStr $ next x+ b = getEntityEnd y+ z = skip isEntityEnd y+ (n,m) = optEntityAttrib (a,b)+ charss t x | t, isEntityNumHex x = second (entityChr x a:) $ charss t z+ where (y,a) = chars $ next x+ z = skip isEntityEnd y+ charss t ((_,w),Pos p:xs) = charss t ((p,w),xs)+ charss t x | Just a <- fromWarn x = charss t $ (if optTagWarning then addWarns [TagWarning $ fromString a] else id) $ next x+ charss t x = (x, [])++ -- utility functions+ next x = second (drop 1) x+ skip f x = assert (isEof x || f x) (next x)+ addWarns ws x@((p,w),y) = ((p, reverse (poss x ws) ++ w), y)+ pos ((p,_),_) rest = if optTagPosition then tagPosition p : rest else rest+ warn x s rest = if optTagWarning then pos x $ TagWarning (fromString s) : rest else rest+ poss x = concatMap (\w -> pos x [w]) +++entityChr x s | isEntityNum x = chr_ $ read s+ | isEntityHex x = chr_ $ fst $ head $ readHex s+ where chr_ x | inRange (toInteger $ ord minBound, toInteger $ ord maxBound) x = chr $ fromInteger x+ | otherwise = '?'+++isEof (_,[]) = True; isEof _ = False+isChar (_,Char{}:_) = True; isChar _ = False+isTag (_,Tag{}:_) = True; isTag _ = False+isTagShut (_,TagShut{}:_) = True; isTagShut _ = False+isAttName (_,AttName{}:_) = True; isAttName _ = False+isAttVal (_,AttVal{}:_) = True; isAttVal _ = False+isTagEnd (_,TagEnd{}:_) = True; isTagEnd _ = False+isTagEndClose (_,TagEndClose{}:_) = True; isTagEndClose _ = False+isComment (_,Comment{}:_) = True; isComment _ = False+isCommentEnd (_,CommentEnd{}:_) = True; isCommentEnd _ = False+isEntityName (_,EntityName{}:_) = True; isEntityName _ = False+isEntityNumHex (_,EntityNum{}:_) = True; isEntityNumHex (_,EntityHex{}:_) = True; isEntityNumHex _ = False+isEntityNum (_,EntityNum{}:_) = True; isEntityNum _ = False+isEntityHex (_,EntityHex{}:_) = True; isEntityHex _ = False+isEntityEnd (_,EntityEnd{}:_) = True; isEntityEnd _ = False+isWarn (_,Warn{}:_) = True; isWarn _ = False++fromChr (_,Char x:_) = Just x ; fromChr _ = Nothing+fromWarn (_,Warn x:_) = Just x ; fromWarn _ = Nothing++getEntityEnd (_,EntityEnd b:_) = b+++-- Merge all adjacent TagText bits+tagTextMerge :: StringLike str => [Tag str] -> [Tag str]+tagTextMerge (TagText x:xs) = TagText (strConcat (x:a)) : tagTextMerge b+ where+ (a,b) = f xs++ -- additional brackets on 3 lines to work around HSE 1.3.2 bugs with pattern fixities+ f (TagText x:xs) = (x:a,b)+ where (a,b) = f xs+ f (TagPosition{}:(x@TagText{}:xs)) = f $ x : xs+ f x = g x id x++ g o op (p@TagPosition{}:(w@TagWarning{}:xs)) = g o (op . (p:) . (w:)) xs+ g o op (w@TagWarning{}:xs) = g o (op . (w:)) xs+ g o op (p@TagPosition{}:(x@TagText{}:xs)) = f $ p : x : op xs+ g o op (x@TagText{}:xs) = f $ x : op xs+ g o op _ = ([], o)++tagTextMerge (x:xs) = x : tagTextMerge xs+tagTextMerge [] = []
+ src/Text/HTML/TagSoup/Manual.hs view
@@ -0,0 +1,13 @@++module Text.HTML.TagSoup.Manual(parseTagsOptions) where++import Text.HTML.TagSoup.Specification+import Text.HTML.TagSoup.Implementation+import Text.HTML.TagSoup.Type+import Text.HTML.TagSoup.Options+import Text.StringLike+++parseTagsOptions :: StringLike str => ParseOptions str -> str -> [Tag str]+parseTagsOptions opts = output opts . parse . toString+
+ src/Text/HTML/TagSoup/Match.hs view
@@ -0,0 +1,198 @@+-- | Combinators to match tags. Some people prefer to use @(~==)@ from+-- "Text.HTML.TagSoup", others prefer these more structured combinators.+-- Which you use is personal preference.+--+-- The functions below offer maximum flexibility for matching tags.+-- Using 'tagOpen', for example, you can match all links or buttons that have the "btn" class.+--+-- For simple uses cases—like matching all comment tags, or matching opening @\<a>@ tags,+-- use the tag identification functions in "Text.HTML.TagSoup#tag-identification".+module Text.HTML.TagSoup.Match where++import Text.HTML.TagSoup.Type (Tag(..), Attribute)+import Data.List (tails)++-- * Matching Tags++-- | Match an opening tag+--+-- ==== __Examples__+--+-- /Matching an opening @\<a>@ tag with a @"btn"@ class:/+--+-- >>> let tag = TagOpen "a" [("class", "btn")]+-- >>> tagOpen (== "a") (\attrs -> any (== ("class", "btn")) attrs) tag+-- True+tagOpen :: (str -> Bool) -> ([Attribute str] -> Bool) -> Tag str -> Bool+tagOpen pName pAttrs (TagOpen name attrs) =+ pName name && pAttrs attrs+tagOpen _ _ _ = False++-- | Match a closing tag+--+-- ==== __Examples__+--+-- /Matching a closing @\<\/a>@ tag:/+--+-- >>> tagClose (== "a") (TagClose "a")+-- True+--+-- >>> tagClose (== "a") (TagOpen "a" [])+-- False+tagClose :: (str -> Bool) -> Tag str -> Bool+tagClose pName (TagClose name) = pName name+tagClose _ _ = False++-- | Match text tags+--+-- ==== __Examples__+--+-- /Match all text tags:/+--+-- >>> let tags = parseTags "<p>This is a paragraph</p>"+-- [TagOpen "p" [],TagText "This is a paragraph",TagClose "p"]+-- >>> filter (tagText (const True)) tags+-- [TagText "This is a paragraph"]+tagText :: (str -> Bool) -> Tag str -> Bool+tagText p (TagText text) = p text+tagText _ _ = False++-- | Match comment tags+--+-- ==== __Examples__+--+-- /Matching comment tags that include an exclamation mark:/+--+-- >>> let tags = parseTags "<!--This is a comment-->"+-- [TagComment "This is a comment!"]+-- >>> all (tagComment (\s -> '!' `elem` s)) tags+-- True+tagComment :: (str -> Bool) -> Tag str -> Bool+tagComment p (TagComment text) = p text+tagComment _ _ = False+++-- | Match an opening tag's name literally+--+-- ==== __Examples__+--+-- /Matching @\<a>@ tags with the @id@ "foo":/+--+-- >>> let tag = TagOpen "a" [("id", "foo")]+-- TagOpen "a" [("id","foo")]+-- >>> tagOpenLit "a" (\attrs -> any (== ("id", "foo")) attrs) tag+-- True+--+tagOpenLit :: Eq str => str -> ([Attribute str] -> Bool) -> Tag str -> Bool+tagOpenLit name = tagOpen (name==)++-- | Match a closing tag's name literally+--+-- ==== __Examples__+--+-- /Match a closing @\<a>@ tag:/+--+-- >>> tagCloseLit "a" (TagClose "a")+-- True+--+-- >>> tagCloseLit "a" (TagClose "em")+-- False+tagCloseLit :: Eq str => str -> Tag str -> Bool+tagCloseLit name = tagClose (name==)++-- | Match an opening tag's name literally, and at least one of its attributes+--+-- ==== __Examples__+--+-- /Matching a @\<div>@ tag with the @id@ "foo":/+--+-- >>> tagOpenAttrLit "div" ("id", "foo") (TagOpen "div" [("id", "foo")])+-- True+tagOpenAttrLit :: Eq str => str -> Attribute str -> Tag str -> Bool+tagOpenAttrLit name attr =+ tagOpenLit name (anyAttrLit attr)++{- |+Match a tag with given name, that contains an attribute+with given name, that satisfies a predicate.+If an attribute occurs multiple times,+all occurrences are checked.++==== __Examples__++/Matching an @\<a>@ tag with an ID that starts with "comment-":/++>>> let commentTag = TagOpen "a" [("id", "comment-45678")]+>>> tagOpenAttrNameLit "a" "id" (\idValue -> "comment-" `Data.List.isPrefixOf` idValue) commentTag+True+-}+tagOpenAttrNameLit :: Eq str => str -> str -> (str -> Bool) -> Tag str -> Bool+tagOpenAttrNameLit tagName attrName pAttrValue =+ tagOpenLit tagName+ (anyAttr (\(name,value) -> name==attrName && pAttrValue value))+++-- | Check if the 'Tag str' is 'TagOpen' and matches the given name+--+-- ==== __Examples__+--+-- /Matching an @\<a>@ tag:/+--+-- >>> tagOpenNameLit "a" (TagOpen "a" [])+-- True+--+-- >>> tagOpenNameLit "a" (TagOpen "div" [])+-- False+tagOpenNameLit :: Eq str => str -> Tag str -> Bool+tagOpenNameLit name = tagOpenLit name (const True)++-- | Check if the 'Tag str' is 'TagClose' and matches the given name+--+-- ==== __Examples__+--+-- /Matching a closing @\<\/a>@ tag:/+--+-- >>> tagCloseNameLit "a" (TagClose "a")+-- True+--+-- >>> tagCloseNameLit "a" (TagClose "div")+-- False+tagCloseNameLit :: Eq str => str -> Tag str -> Bool+tagCloseNameLit name = tagCloseLit name+++-- * Matching attributes++-- | Does any attribute name/value match the predicate.+anyAttr :: ((str,str) -> Bool) -> [Attribute str] -> Bool+anyAttr = any++-- | Does any attribute name match the predicate.+anyAttrName :: (str -> Bool) -> [Attribute str] -> Bool+anyAttrName p = any (p . fst)++-- | Does any attribute value match the predicate.+anyAttrValue :: (str -> Bool) -> [Attribute str] -> Bool+anyAttrValue p = any (p . snd)+++-- | Does any attribute name/value match.+anyAttrLit :: Eq str => (str,str) -> [Attribute str] -> Bool+anyAttrLit attr = anyAttr (attr==)++-- | Does any attribute name match.+anyAttrNameLit :: Eq str => str -> [Attribute str] -> Bool+anyAttrNameLit name = anyAttrName (name==)++-- | Does any attribute value match.+anyAttrValueLit :: Eq str => str -> [Attribute str] -> Bool+anyAttrValueLit value = anyAttrValue (value==)++++-- | Get the tags under tags with a given name where the attributes match some predicate.+getTagContent :: Eq str => str -> ([Attribute str] -> Bool) -> [Tag str] -> [Tag str]+getTagContent name pAttrs =+ takeWhile (not . tagCloseLit name) . drop 1 .+ head . sections (tagOpenLit name pAttrs)+ where sections p = filter (p . head) . init . tails
+ src/Text/HTML/TagSoup/Options.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE DeriveDataTypeable #-}++module Text.HTML.TagSoup.Options where++import Data.Typeable+import Text.HTML.TagSoup.Type+import Text.HTML.TagSoup.Entity+import Text.StringLike+++-- | These options control how 'parseTags' works. The 'ParseOptions' type is usually generated by one of+-- 'parseOptions', 'parseOptionsFast' or 'parseOptionsEntities', then selected fields may be overriden.+--+-- The options 'optTagPosition' and 'optTagWarning' specify whether to generate+-- 'TagPosition' or 'TagWarning' elements respectively. Usually these options should be set to @False@+-- to simplify future stages, unless you rely on position information or want to give malformed HTML+-- messages to the end user.+--+-- The options 'optEntityData' and 'optEntityAttrib' control how entities, for example @ @ are handled.+-- Both take a string, and a boolean, where @True@ indicates that the entity ended with a semi-colon @;@.+-- Inside normal text 'optEntityData' will be called, and the results will be inserted in the tag stream.+-- Inside a tag attribute 'optEntityAttrib' will be called, and the first component of the result will be used+-- in the attribute, and the second component will be appended after the 'TagOpen' value (usually the second+-- component is @[]@). As an example, to not decode any entities, pass:+--+-- > parseOptions+-- > {optEntityData=\(str,b) -> [TagText $ "&" ++ str ++ [';' | b]]+-- > ,optEntityAttrib\(str,b) -> ("&" ++ str ++ [';' | b], [])++-- The 'optTagTextMerge' value specifies if you always want adjacent 'TagText' values to be merged.+-- Merging adjacent pieces of text has a small performance penalty, but will usually make subsequent analysis+-- simpler. Contiguous runs of characters without entities or tags will also be generated as single 'TagText'+-- values.+data ParseOptions str = ParseOptions+ {optTagPosition :: Bool -- ^ Should 'TagPosition' values be given before some items (default=False,fast=False).+ ,optTagWarning :: Bool -- ^ Should 'TagWarning' values be given (default=False,fast=False)+ ,optEntityData :: (str,Bool) -> [Tag str] -- ^ How to lookup an entity (Bool = has ending @';'@)+ ,optEntityAttrib :: (str,Bool) -> (str,[Tag str]) -- ^ How to lookup an entity in an attribute (Bool = has ending @';'@?)+ ,optTagTextMerge :: Bool -- ^ Require no adjacent 'TagText' values (default=True,fast=False)+ }+ deriving Typeable+++-- | A 'ParseOptions' structure using a custom function to lookup attributes. Any attribute+-- that is not found will be left intact, and a 'TagWarning' given (if 'optTagWarning' is set).+--+-- If you do not want to resolve any entities, simpliy pass @const Nothing@ for the lookup function.+parseOptionsEntities :: StringLike str => (str -> Maybe str) -> ParseOptions str+parseOptionsEntities lookupEntity = ParseOptions False False entityData entityAttrib True+ where+ entityData x = TagText a : b+ where (a,b) = entityAttrib x++ entityAttrib ~(x,b) =+ let x' = x `append` fromString [';'|b]+ in case lookupEntity x' of+ Just y -> (y, [])+ Nothing -> (fromChar '&' `append` x'+ ,[TagWarning $ fromString "Unknown entity: " `append` x])+++-- | The default parse options value, described in 'ParseOptions'. Equivalent to+-- @'parseOptionsEntities' 'lookupEntity'@.+parseOptions :: StringLike str => ParseOptions str+parseOptions = parseOptionsEntities $ fmap fromString . lookupEntity . toString+++-- | A 'ParseOptions' structure optimised for speed, following the fast options.+parseOptionsFast :: StringLike str => ParseOptions str+parseOptionsFast = parseOptions{optTagTextMerge=False}+++-- | Change the underlying string type of a 'ParseOptions' value.+fmapParseOptions :: (StringLike from, StringLike to) => ParseOptions from -> ParseOptions to+fmapParseOptions (ParseOptions a b c d e) = ParseOptions a b c2 d2 e+ where+ c2 ~(x,y) = map (fmap castString) $ c (castString x, y)+ d2 ~(x,y) = (castString r, map (fmap castString) s)+ where (r,s) = d (castString x, y)+
+ src/Text/HTML/TagSoup/Parser.hs view
@@ -0,0 +1,25 @@++module Text.HTML.TagSoup.Parser(+ parseTags, parseTagsOptions,+ ParseOptions(..), parseOptions, parseOptionsFast, parseOptionsEntities+ ) where++import Text.HTML.TagSoup.Type+import Text.HTML.TagSoup.Options+import qualified Text.HTML.TagSoup.Generated as Gen+++-- | Parse a string to a list of tags, using an HTML 5 compliant parser.+--+-- > parseTags "<hello>my&</world>" == [TagOpen "hello" [],TagText "my&",TagClose "world"]+parseTags :: StringLike str => str -> [Tag str]+parseTags = parseTagsOptions parseOptions+++-- | Parse a string to a list of tags, using settings supplied by the 'ParseOptions' parameter,+-- eg. to output position information:+--+-- > parseTagsOptions parseOptions{optTagPosition = True} "<hello>my&</world>" ==+-- > [TagPosition 1 1,TagOpen "hello" [],TagPosition 1 8,TagText "my&",TagPosition 1 15,TagClose "world"]+parseTagsOptions :: StringLike str => ParseOptions str -> str -> [Tag str]+parseTagsOptions = Gen.parseTagsOptions
+ src/Text/HTML/TagSoup/Render.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE PatternGuards, OverloadedStrings #-}+{-|+ This module converts a list of 'Tag' back into a string.+-}++module Text.HTML.TagSoup.Render+ (+ renderTags, renderTagsOptions, escapeHTML,+ RenderOptions(..), renderOptions+ ) where++import Text.HTML.TagSoup.Entity+import Text.HTML.TagSoup.Type+import Text.StringLike+++-- | These options control how 'renderTags' works.+--+-- The strange quirk of only minimizing @\<br\>@ tags is due to Internet Explorer treating+-- @\<br\>\<\/br\>@ as @\<br\>\<br\>@.+data RenderOptions str = RenderOptions+ {optEscape :: str -> str -- ^ Escape a piece of text (default = escape the four characters @&\"\<\>@)+ ,optMinimize :: str -> Bool -- ^ Minimise \<b\>\<\/b\> -> \<b/\> (default = minimise only @\<br\>@ tags)+ ,optRawTag :: str -> Bool -- ^ Should a tag be output with no escaping (default = true only for @script@)+ }+++-- | Replace the four characters @&\"\<\>@ with their HTML entities ('escapeXML' lifted to 'StringLike').+escapeHTML :: StringLike str => str -> str+escapeHTML = fromString . escapeXML . toString++-- | The default render options value, described in 'RenderOptions'.+renderOptions :: StringLike str => RenderOptions str+renderOptions = RenderOptions escapeHTML (\x -> toString x == "br") (\x -> toString x == "script")+++-- | Show a list of tags, as they might have been parsed, using the default settings given in+-- 'RenderOptions'.+--+-- > renderTags [TagOpen "hello" [],TagText "my&",TagClose "world"] == "<hello>my&</world>"+renderTags :: StringLike str => [Tag str] -> str+renderTags = renderTagsOptions renderOptions+++-- | Show a list of tags using settings supplied by the 'RenderOptions' parameter,+-- eg. to avoid escaping any characters one could do:+--+-- > renderTagsOptions renderOptions{optEscape = id} [TagText "my&"] == "my&"+renderTagsOptions :: StringLike str => RenderOptions str -> [Tag str] -> str+renderTagsOptions opts = strConcat . tags+ where+ ss x = [x]++ tags (TagOpen name atts:TagClose name2:xs)+ | name == name2 && optMinimize opts name = open name atts " /" ++ tags xs+ tags (TagOpen name atts:xs)+ | Just ('?',_) <- uncons name = open name atts " ?" ++ tags xs+ | optRawTag opts name =+ let (a,b) = break (== TagClose name) (TagOpen name atts:xs)+ in concatMap (\x -> case x of TagText s -> [s]; _ -> tag x) a ++ tags b+ tags (x:xs) = tag x ++ tags xs+ tags [] = []++ tag (TagOpen name atts) = open name atts ""+ tag (TagClose name) = ["</", name, ">"]+ tag (TagText text) = [txt text]+ tag (TagComment text) = ss "<!--" ++ com text ++ ss "-->"+ tag _ = ss ""++ txt = optEscape opts+ open name atts shut = ["<",name] ++ concatMap att atts ++ [shut,">"]+ att ("","") = [" \"\""]+ att (x ,"") = [" ", x]+ att ("", y) = [" \"",txt y,"\""]+ att (x , y) = [" ",x,"=\"",txt y,"\""]++ com xs | Just ('-',xs) <- uncons xs, Just ('-',xs) <- uncons xs, Just ('>',xs) <- uncons xs = "-- >" : com xs+ com xs = case uncons xs of+ Nothing -> []+ Just (x,xs) -> fromChar x : com xs
+ src/Text/HTML/TagSoup/Specification.hs view
@@ -0,0 +1,332 @@+{-# LANGUAGE RecordWildCards, PatternGuards #-}++module Text.HTML.TagSoup.Specification(parse) where++import Text.HTML.TagSoup.Implementation+import Data.Char (isAlpha, isAlphaNum, isDigit, toLower)++-- We make some generalisations:+-- <!name is a valid tag start closed by >+-- <?name is a valid tag start closed by ?>+-- </!name> is a valid closing tag+-- </?name> is a valid closing tag+-- <a "foo"> is a valid tag attibute in ! and ?, i.e missing an attribute name+-- We also don't do lowercase conversion+-- Entities are handled without a list of known entity names+-- We don't have RCData, CData or Escape modes (only effects dat and tagOpen)+++data TypeTag = TypeNormal -- <foo+ | TypeXml -- <?foo+ | TypeDecl -- <!foo+ | TypeScript -- <script+ deriving Eq+++-- 2.4.1 Common parser idioms+white x = x `elem` " \t\n\f\r"+++-- 8.2.4 Tokenization++type Parser = S -> [Out]++parse :: String -> [Out]+parse = dat . state ++-- 8.2.4.1 Data state+dat :: Parser+dat S{..} = pos $ case hd of+ '&' -> charReference tl+ '<' -> tagOpen tl+ _ | eof -> []+ _ -> hd & dat tl+++-- 8.2.4.2 Character reference data state+charReference s = charRef dat False Nothing s+++-- 8.2.4.3 Tag open state+tagOpen S{..} = case hd of+ '!' -> markupDeclOpen tl+ '/' -> closeTagOpen tl+ _ | isAlpha hd -> Tag & hd & tagName (if isScript s then TypeScript else TypeNormal) tl+ '>' -> errSeen "<>" & '<' & '>' & dat tl+ '?' -> neilXmlTagOpen tl -- NEIL+ _ -> errSeen "<" & '<' & dat s++isScript = f "script"+ where+ f (c:cs) S{..} = toLower hd == c && f cs tl+ f [] S{..} = white hd || hd == '/' || hd == '>' || hd == '?' || eof+++-- seen "<?", emitted []+neilXmlTagOpen S{..} = pos $ case hd of+ _ | isAlpha hd -> Tag & '?' & hd & tagName TypeXml tl+ _ -> errSeen "<?" & '<' & '?' & dat s++-- seen "?", expecting ">"+neilXmlTagClose S{..} = pos $ case hd of+ '>' -> TagEnd & dat tl+ _ -> errSeen "?" & beforeAttName TypeXml s+++-- just seen ">" at the end, am given tl+neilTagEnd typ S{..}+ | typ == TypeXml = pos $ errWant "?>" & TagEnd & dat s+ | typ == TypeScript = pos $ TagEnd & neilScriptBody s+ | otherwise = pos $ TagEnd & dat s++-- Inside a <script> tag, only break on </script+neilScriptBody o@S{..}+ | hd == '<', S{..} <- tl+ , hd == '/', S{..} <- tl+ , isScript s+ = dat o+ | eof = []+ | otherwise = pos $ hd & neilScriptBody tl+++-- 8.2.4.4 Close tag open state+-- Deviation: We ignore the if CDATA/RCDATA bits and tag matching+-- Deviation: On </> we output </> to the text+-- Deviation: </!name> is a closing tag, not a bogus comment+closeTagOpen S{..} = case hd of+ _ | isAlpha hd || hd `elem` "?!" -> TagShut & hd & tagName TypeNormal tl+ '>' -> errSeen "</>" & '<' & '/' & '>' & dat tl+ _ | eof -> '<' & '/' & dat s+ _ -> errWant "tag name" & bogusComment s+++-- 8.2.4.5 Tag name state+tagName typ S{..} = pos $ case hd of+ _ | white hd -> beforeAttName typ tl+ '/' -> selfClosingStartTag typ tl+ '>' -> neilTagEnd typ tl+ '?' | typ == TypeXml -> neilXmlTagClose tl+ _ | isAlpha hd -> hd & tagName typ tl+ _ | eof -> errWant (if typ == TypeXml then "?>" else ">") & dat s+ _ -> hd & tagName typ tl+++-- 8.2.4.6 Before attribute name state+beforeAttName typ S{..} = pos $ case hd of+ _ | white hd -> beforeAttName typ tl+ '/' -> selfClosingStartTag typ tl+ '>' -> neilTagEnd typ tl+ '?' | typ == TypeXml -> neilXmlTagClose tl+ _ | typ /= TypeNormal && hd `elem` "\'\"" -> beforeAttValue typ s -- NEIL+ _ | hd `elem` "\"'<=" -> errSeen [hd] & AttName & hd & attName typ tl+ _ | eof -> errWant (if typ == TypeXml then "?>" else ">") & dat s+ _ -> AttName & hd & attName typ tl+++-- 8.2.4.7 Attribute name state+attName typ S{..} = pos $ case hd of+ _ | white hd -> afterAttName typ tl+ '/' -> selfClosingStartTag typ tl+ '=' -> beforeAttValue typ tl+ '>' -> neilTagEnd typ tl+ '?' | typ == TypeXml -> neilXmlTagClose tl+ _ | hd `elem` "\"'<" -> errSeen [hd] & def+ _ | eof -> errWant (if typ == TypeXml then "?>" else ">") & dat s+ _ -> def+ where def = hd & attName typ tl+++-- 8.2.4.8 After attribute name state+afterAttName typ S{..} = pos $ case hd of+ _ | white hd -> afterAttName typ tl+ '/' -> selfClosingStartTag typ tl+ '=' -> beforeAttValue typ tl+ '>' -> neilTagEnd typ tl+ '?' | typ == TypeXml -> neilXmlTagClose tl+ _ | typ /= TypeNormal && hd `elem` "\"'" -> AttVal & beforeAttValue typ s -- NEIL+ _ | hd `elem` "\"'<" -> errSeen [hd] & def+ _ | eof -> errWant (if typ == TypeXml then "?>" else ">") & dat s+ _ -> def+ where def = AttName & hd & attName typ tl++-- 8.2.4.9 Before attribute value state+beforeAttValue typ S{..} = pos $ case hd of+ _ | white hd -> beforeAttValue typ tl+ '\"' -> AttVal & attValueDQuoted typ tl+ '&' -> AttVal & attValueUnquoted typ s+ '\'' -> AttVal & attValueSQuoted typ tl+ '>' -> errSeen "=" & neilTagEnd typ tl+ '?' | typ == TypeXml -> neilXmlTagClose tl+ _ | hd `elem` "<=" -> errSeen [hd] & def+ _ | eof -> errWant (if typ == TypeXml then "?>" else ">") & dat s+ _ -> def+ where def = AttVal & hd & attValueUnquoted typ tl+++-- 8.2.4.10 Attribute value (double-quoted) state+attValueDQuoted typ S{..} = pos $ case hd of+ '\"' -> afterAttValueQuoted typ tl+ '&' -> charRefAttValue (attValueDQuoted typ) (Just '\"') tl+ _ | eof -> errWant "\"" & dat s+ _ -> hd & attValueDQuoted typ tl+++-- 8.2.4.11 Attribute value (single-quoted) state+attValueSQuoted typ S{..} = pos $ case hd of+ '\'' -> afterAttValueQuoted typ tl+ '&' -> charRefAttValue (attValueSQuoted typ) (Just '\'') tl+ _ | eof -> errWant "\'" & dat s+ _ -> hd & attValueSQuoted typ tl+++-- 8.2.4.12 Attribute value (unquoted) state+attValueUnquoted typ S{..} = pos $ case hd of+ _ | white hd -> beforeAttName typ tl+ '&' -> charRefAttValue (attValueUnquoted typ) Nothing tl+ '>' -> neilTagEnd typ tl+ '?' | typ == TypeXml -> neilXmlTagClose tl+ _ | hd `elem` "\"'<=" -> errSeen [hd] & def+ _ | eof -> errWant (if typ == TypeXml then "?>" else ">") & dat s+ _ -> def+ where def = hd & attValueUnquoted typ tl+++-- 8.2.4.13 Character reference in attribute value state+charRefAttValue :: Parser -> Maybe Char -> Parser+charRefAttValue resume c s = charRef resume True c s+++-- 8.2.4.14 After attribute value (quoted) state+afterAttValueQuoted typ S{..} = pos $ case hd of+ _ | white hd -> beforeAttName typ tl+ '/' -> selfClosingStartTag typ tl+ '>' -> neilTagEnd typ tl+ '?' | typ == TypeXml -> neilXmlTagClose tl+ _ | eof -> dat s+ _ -> errSeen [hd] & beforeAttName typ s+++-- 8.2.4.15 Self-closing start tag state+selfClosingStartTag typ S{..} = pos $ case hd of+ _ | typ == TypeXml -> errSeen "/" & beforeAttName typ s+ '>' -> TagEndClose & dat tl+ _ | eof -> errWant ">" & dat s+ _ -> errSeen "/" & beforeAttName typ s+++-- 8.2.4.16 Bogus comment state+bogusComment S{..} = Comment & bogusComment1 s+bogusComment1 S{..} = pos $ case hd of+ '>' -> CommentEnd & dat tl+ _ | eof -> CommentEnd & dat s+ _ -> hd & bogusComment1 tl+++-- 8.2.4.17 Markup declaration open state+markupDeclOpen S{..} = pos $ case hd of+ _ | Just s <- next "--" -> Comment & commentStart s+ _ | isAlpha hd -> Tag & '!' & hd & tagName TypeDecl tl -- NEIL+ _ | Just s <- next "[CDATA[" -> cdataSection s+ _ -> errWant "tag name" & bogusComment s+++-- 8.2.4.18 Comment start state+commentStart S{..} = pos $ case hd of+ '-' -> commentStartDash tl+ '>' -> errSeen "<!-->" & CommentEnd & dat tl+ _ | eof -> errWant "-->" & CommentEnd & dat s+ _ -> hd & comment tl+++-- 8.2.4.19 Comment start dash state+commentStartDash S{..} = pos $ case hd of+ '-' -> commentEnd tl+ '>' -> errSeen "<!--->" & CommentEnd & dat tl+ _ | eof -> errWant "-->" & CommentEnd & dat s+ _ -> '-' & hd & comment tl+++-- 8.2.4.20 Comment state+comment S{..} = pos $ case hd of+ '-' -> commentEndDash tl+ _ | eof -> errWant "-->" & CommentEnd & dat s+ _ -> hd & comment tl+++-- 8.2.4.21 Comment end dash state+commentEndDash S{..} = pos $ case hd of+ '-' -> commentEnd tl+ _ | eof -> errWant "-->" & CommentEnd & dat s+ _ -> '-' & hd & comment tl+++-- 8.2.4.22 Comment end state+commentEnd S{..} = pos $ case hd of+ '>' -> CommentEnd & dat tl+ '-' -> errWant "-->" & '-' & commentEnd tl+ _ | white hd -> errSeen "--" & '-' & '-' & hd & commentEndSpace tl+ '!' -> errSeen "!" & commentEndBang tl+ _ | eof -> errWant "-->" & CommentEnd & dat s+ _ -> errSeen "--" & '-' & '-' & hd & comment tl+++-- 8.2.4.23 Comment end bang state+commentEndBang S{..} = pos $ case hd of+ '>' -> CommentEnd & dat tl+ '-' -> '-' & '-' & '!' & commentEndDash tl+ _ | eof -> errWant "-->" & CommentEnd & dat s+ _ -> '-' & '-' & '!' & hd & comment tl+++-- 8.2.4.24 Comment end space state+commentEndSpace S{..} = pos $ case hd of+ '>' -> CommentEnd & dat tl+ '-' -> commentEndDash tl+ _ | white hd -> hd & commentEndSpace tl+ _ | eof -> errWant "-->" & CommentEnd & dat s+ _ -> hd & comment tl+++-- 8.2.4.38 CDATA section state+cdataSection S{..} = pos $ case hd of+ _ | Just s <- next "]]>" -> dat s+ _ | eof -> dat s+ _ | otherwise -> hd & cdataSection tl+++-- 8.2.4.39 Tokenizing character references+-- Change from spec: this is reponsible for writing '&' if nothing is to be written+charRef :: Parser -> Bool -> Maybe Char -> S -> [Out]+charRef resume att end S{..} = case hd of+ _ | eof || hd `elem` "\t\n\f <&" || maybe False (== hd) end -> '&' & resume s+ '#' -> charRefNum resume s tl+ _ -> charRefAlpha resume att s++charRefNum resume o S{..} = case hd of+ _ | hd `elem` "xX" -> charRefNum2 resume o True tl+ _ -> charRefNum2 resume o False s++charRefNum2 resume o hex S{..} = case hd of+ _ | hexChar hex hd -> (if hex then EntityHex else EntityNum) & hd & charRefNum3 resume hex tl+ _ -> errSeen "&" & '&' & resume o++charRefNum3 resume hex S{..} = case hd of+ _ | hexChar hex hd -> hd & charRefNum3 resume hex tl+ ';' -> EntityEnd True & resume tl+ _ -> EntityEnd False & errWant ";" & resume s++charRefAlpha resume att S{..} = case hd of+ _ | isAlpha hd -> EntityName & hd & charRefAlpha2 resume att tl+ _ -> errSeen "&" & '&' & resume s++charRefAlpha2 resume att S{..} = case hd of+ _ | alphaChar hd -> hd & charRefAlpha2 resume att tl+ ';' -> EntityEnd True & resume tl+ _ | att -> EntityEnd False & resume s+ _ -> EntityEnd False & errWant ";" & resume s+++alphaChar x = isAlphaNum x || x `elem` ":-_"++hexChar False x = isDigit x+hexChar True x = isDigit x || (x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F')
+ src/Text/HTML/TagSoup/Tree.hs view
@@ -0,0 +1,118 @@+{-|+ /NOTE/: This module is preliminary and may change at a future date.++ This module is intended to help converting a list of tags into a+ tree of tags.+-}++module Text.HTML.TagSoup.Tree+ (+ TagTree(..), tagTree, parseTree, parseTreeOptions, ParseOptions(..),+ flattenTree, renderTree, renderTreeOptions, RenderOptions(..), transformTree, universeTree+ ) where++import Text.HTML.TagSoup (parseTags, parseTagsOptions, renderTags, renderTagsOptions, ParseOptions(..), RenderOptions(..))+import Text.HTML.TagSoup.Type+import Control.Arrow+import GHC.Exts (build)+++-- | A tree of 'Tag' values.+data TagTree str+ = -- | A 'TagOpen'/'TagClose' pair with the 'Tag' values in between.+ TagBranch str [Attribute str] [TagTree str]+ | -- | Any leaf node+ TagLeaf (Tag str)+ deriving (Eq,Ord,Show)++instance Functor TagTree where+ fmap f (TagBranch x y z) = TagBranch (f x) (map (f***f) y) (map (fmap f) z)+ fmap f (TagLeaf x) = TagLeaf (fmap f x)+++-- | Convert a list of tags into a tree. This version is not lazy at+-- all, that is saved for version 2.+tagTree :: Eq str => [Tag str] -> [TagTree str]+tagTree = g+ where+ g :: Eq str => [Tag str] -> [TagTree str]+ g [] = []+ g xs = a ++ map TagLeaf (take 1 b) ++ g (drop 1 b)+ where (a,b) = f xs++ -- the second tuple is either null or starts with a close+ f :: Eq str => [Tag str] -> ([TagTree str],[Tag str])+ f (TagOpen name atts:rest) =+ case f rest of+ (inner,[]) -> (TagLeaf (TagOpen name atts):inner, [])+ (inner,TagClose x:xs)+ | x == name -> let (a,b) = f xs in (TagBranch name atts inner:a, b)+ | otherwise -> (TagLeaf (TagOpen name atts):inner, TagClose x:xs)+ _ -> error "TagSoup.Tree.tagTree: safe as - forall x . isTagClose (snd (f x))"++ f (TagClose x:xs) = ([], TagClose x:xs)+ f (x:xs) = (TagLeaf x:a,b)+ where (a,b) = f xs+ f [] = ([], [])++-- | Build a 'TagTree' from a string.+parseTree :: StringLike str => str -> [TagTree str]+parseTree = tagTree . parseTags++-- | Build a 'TagTree' from a string, specifying the 'ParseOptions'.+parseTreeOptions :: StringLike str => ParseOptions str -> str -> [TagTree str]+parseTreeOptions opts str = tagTree $ parseTagsOptions opts str++-- | Flatten a 'TagTree' back to a list of 'Tag'.+flattenTree :: [TagTree str] -> [Tag str]+flattenTree xs = build $ flattenTreeFB xs++flattenTreeFB :: [TagTree str] -> (Tag str -> lst -> lst) -> lst -> lst+flattenTreeFB xs cons nil = flattenTreeOnto xs nil+ where+ flattenTreeOnto [] tags = tags+ flattenTreeOnto (TagBranch name atts inner:trs) tags =+ TagOpen name atts `cons` flattenTreeOnto inner (TagClose name `cons` flattenTreeOnto trs tags)+ flattenTreeOnto (TagLeaf x:trs) tags = x `cons` flattenTreeOnto trs tags++-- | Render a 'TagTree'.+renderTree :: StringLike str => [TagTree str] -> str+renderTree = renderTags . flattenTree++-- | Render a 'TagTree' with some 'RenderOptions'.+renderTreeOptions :: StringLike str => RenderOptions str -> [TagTree str] -> str+renderTreeOptions opts trees = renderTagsOptions opts $ flattenTree trees++-- | This operation is based on the Uniplate @universe@ function. Given a+-- list of trees, it returns those trees, and all the children trees at+-- any level. For example:+--+-- > universeTree+-- > [TagBranch "a" [("href","url")] [TagBranch "b" [] [TagLeaf (TagText "text")]]]+-- > == [TagBranch "a" [("href","url")] [TagBranch "b" [] [TagLeaf (TagText "text")]]]+-- > ,TagBranch "b" [] [TagLeaf (TagText "text")]]+--+-- This operation is particularly useful for queries. To collect all @\"a\"@+-- tags in a tree, simply do:+--+-- > [x | x@(TagBranch "a" _ _) <- universeTree tree]+universeTree :: [TagTree str] -> [TagTree str]+universeTree = concatMap f+ where+ f t@(TagBranch _ _ inner) = t : universeTree inner+ f x = [x]+++-- | This operation is based on the Uniplate @transform@ function. Given a+-- list of trees, it applies the function to every tree in a bottom-up+-- manner. This operation is useful for manipulating a tree - for example+-- to make all tag names upper case:+--+-- > upperCase = transformTree f+-- > where f (TagBranch name atts inner) = [TagBranch (map toUpper name) atts inner]+-- > f x = [x]+transformTree :: (TagTree str -> [TagTree str]) -> [TagTree str] -> [TagTree str]+transformTree act = concatMap f+ where+ f (TagBranch a b inner) = act $ TagBranch a b (transformTree act inner)+ f x = act x
+ src/Text/HTML/TagSoup/Type.hs view
@@ -0,0 +1,142 @@+{-# LANGUAGE DeriveDataTypeable #-}+-- | The central type in TagSoup++module Text.HTML.TagSoup.Type(+ -- * Data structures and parsing+ StringLike, Tag(..), Attribute, Row, Column,+ + -- * Position manipulation+ Position(..), tagPosition, nullPosition, positionChar, positionString,++ -- * Tag identification+ isTagOpen, isTagClose, isTagText, isTagWarning, isTagPosition,+ isTagOpenName, isTagCloseName, isTagComment,++ -- * Extraction+ fromTagText, fromAttrib,+ maybeTagText, maybeTagWarning,+ innerText,+ ) where+++import Data.List (foldl')+import Data.Maybe (fromMaybe, mapMaybe)+import Text.StringLike+import Data.Data(Data, Typeable)++-- | An HTML attribute @id=\"name\"@ generates @(\"id\",\"name\")@+type Attribute str = (str,str)++-- | The row/line of a position, starting at 1+type Row = Int++-- | The column of a position, starting at 1+type Column = Int+++--- All positions are stored as a row and a column, with (1,1) being the+--- top-left position+data Position = Position !Row !Column deriving (Show,Eq,Ord)++nullPosition :: Position+nullPosition = Position 1 1++positionString :: Position -> String -> Position+positionString = foldl' positionChar++positionChar :: Position -> Char -> Position+positionChar (Position r c) x = case x of+ '\n' -> Position (r+1) 1+ '\t' -> Position r (c + 8 - mod (c-1) 8)+ _ -> Position r (c+1)++tagPosition :: Position -> Tag str+tagPosition (Position r c) = TagPosition r c+++-- | A single HTML element. A whole document is represented by a list of @Tag@.+-- There is no requirement for 'TagOpen' and 'TagClose' to match.+data Tag str =+ TagOpen str [Attribute str] -- ^ An open tag with 'Attribute's in their original order+ | TagClose str -- ^ A closing tag+ | TagText str -- ^ A text node, guaranteed not to be the empty string+ | TagComment str -- ^ A comment+ | TagWarning str -- ^ Meta: A syntax error in the input file+ | TagPosition !Row !Column -- ^ Meta: The position of a parsed element+ deriving (Show, Eq, Ord, Data, Typeable)++instance Functor Tag where+ fmap f (TagOpen x y) = TagOpen (f x) [(f a, f b) | (a,b) <- y]+ fmap f (TagClose x) = TagClose (f x)+ fmap f (TagText x) = TagText (f x)+ fmap f (TagComment x) = TagComment (f x)+ fmap f (TagWarning x) = TagWarning (f x)+ fmap f (TagPosition x y) = TagPosition x y+++-- | Test if a 'Tag' is a 'TagOpen'+isTagOpen :: Tag str -> Bool+isTagOpen (TagOpen {}) = True; isTagOpen _ = False++-- | Test if a 'Tag' is a 'TagClose'+isTagClose :: Tag str -> Bool+isTagClose (TagClose {}) = True; isTagClose _ = False++-- | Test if a 'Tag' is a 'TagText'+isTagText :: Tag str -> Bool+isTagText (TagText {}) = True; isTagText _ = False++-- | Extract the string from within 'TagText', otherwise 'Nothing'+maybeTagText :: Tag str -> Maybe str+maybeTagText (TagText x) = Just x+maybeTagText _ = Nothing++-- | Extract the string from within 'TagText', crashes if not a 'TagText'+fromTagText :: Show str => Tag str -> str+fromTagText (TagText x) = x+fromTagText x = error $ "(" ++ show x ++ ") is not a TagText"++-- | Extract all text content from tags (similar to Verbatim found in HaXml)+innerText :: StringLike str => [Tag str] -> str+innerText = strConcat . mapMaybe maybeTagText++-- | Test if a 'Tag' is a 'TagWarning'+isTagWarning :: Tag str -> Bool+isTagWarning (TagWarning {}) = True; isTagWarning _ = False++-- | Extract the string from within 'TagWarning', otherwise 'Nothing'+maybeTagWarning :: Tag str -> Maybe str+maybeTagWarning (TagWarning x) = Just x+maybeTagWarning _ = Nothing++-- | Test if a 'Tag' is a 'TagPosition'+isTagPosition :: Tag str -> Bool+isTagPosition TagPosition{} = True; isTagPosition _ = False++-- | Extract an attribute, crashes if not a 'TagOpen'.+-- Returns @\"\"@ if no attribute present.+--+-- Warning: does not distinquish between missing attribute+-- and present attribute with value @\"\"@.+fromAttrib :: (Show str, Eq str, StringLike str) => str -> Tag str -> str+fromAttrib att tag = fromMaybe empty $ maybeAttrib att tag++-- | Extract an attribute, crashes if not a 'TagOpen'.+-- Returns @Nothing@ if no attribute present.+maybeAttrib :: (Show str, Eq str) => str -> Tag str -> Maybe str+maybeAttrib att (TagOpen _ atts) = lookup att atts+maybeAttrib _ x = error ("(" ++ show x ++ ") is not a TagOpen")++-- | Returns True if the 'Tag' is 'TagOpen' and matches the given name+isTagOpenName :: Eq str => str -> Tag str -> Bool+isTagOpenName name (TagOpen n _) = n == name+isTagOpenName _ _ = False++-- | Returns True if the 'Tag' is 'TagClose' and matches the given name+isTagCloseName :: Eq str => str -> Tag str -> Bool+isTagCloseName name (TagClose n) = n == name+isTagCloseName _ _ = False++-- | Test if a 'Tag' is a 'TagComment'+isTagComment :: Tag str -> Bool+isTagComment TagComment {} = True; isTagComment _ = False
+ src/Text/StringLike.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}++-- | /WARNING/: This module is /not/ intended for use outside the TagSoup library.+--+-- This module provides an abstraction for String's as used inside TagSoup. It allows+-- TagSoup to work with String (list of Char), ByteString.Char8, ByteString.Lazy.Char8,+-- Data.Text and Data.Text.Lazy.+module Text.StringLike (StringLike(..), fromString, castString) where++import Data.String+import Data.Typeable++import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Lazy.Char8 as LBS+import qualified Data.Text as T+import qualified Data.Text.Lazy as LT+++-- | A class to generalise TagSoup parsing over many types of string-like types.+-- Examples are given for the String type.+class (Typeable a, Eq a, IsString a) => StringLike a where+ -- | > empty = ""+ empty :: a+ -- | > cons = (:)+ cons :: Char -> a -> a+ -- | > uncons [] = Nothing+ -- > uncons (x:xs) = Just (x, xs)+ uncons :: a -> Maybe (Char, a)++ -- | > toString = id+ toString :: a -> String+ -- | > fromChar = return+ fromChar :: Char -> a+ -- | > strConcat = concat+ strConcat :: [a] -> a+ -- | > strNull = null+ strNull :: a -> Bool+ -- | > append = (++)+ append :: a -> a -> a+++-- | Convert a String from one type to another.+castString :: (StringLike a, StringLike b) => a -> b+castString = fromString . toString+++instance StringLike String where+ uncons [] = Nothing+ uncons (x:xs) = Just (x, xs)+ toString = id+ fromChar = (:[])+ strConcat = concat+ empty = []+ strNull = null+ cons c = (c:)+ append = (++)++instance StringLike BS.ByteString where+ uncons = BS.uncons+ toString = BS.unpack+ fromChar = BS.singleton+ strConcat = BS.concat+ empty = BS.empty+ strNull = BS.null+ cons = BS.cons+ append = BS.append++instance StringLike LBS.ByteString where+ uncons = LBS.uncons+ toString = LBS.unpack+ fromChar = LBS.singleton+ strConcat = LBS.concat+ empty = LBS.empty+ strNull = LBS.null+ cons = LBS.cons+ append = LBS.append++instance StringLike T.Text where+ uncons = T.uncons+ toString = T.unpack+ fromChar = T.singleton+ strConcat = T.concat+ empty = T.empty+ strNull = T.null+ cons = T.cons+ append = T.append++instance StringLike LT.Text where+ uncons = LT.uncons+ toString = LT.unpack+ fromChar = LT.singleton+ strConcat = LT.concat+ empty = LT.empty+ strNull = LT.null+ cons = LT.cons+ append = LT.append
tagsoup.cabal view
@@ -1,6 +1,6 @@ cabal-version: >= 1.18 name: tagsoup-version: 0.14.1+version: 0.14.2 copyright: Neil Mitchell 2006-2017 author: Neil Mitchell <ndmitchell@gmail.com> maintainer: Neil Mitchell <ndmitchell@gmail.com>@@ -11,7 +11,7 @@ license-file: LICENSE build-type: Simple synopsis: Parsing and extracting information from (possibly malformed) HTML/XML documents-tested-with: GHC==8.0.2, GHC==7.10.3, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2+tested-with: GHC==8.2.1, GHC==8.0.2, GHC==7.10.3, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2 description: TagSoup is a library for parsing HTML/XML. It supports the HTML 5 specification, and can be used to parse either well-formed XML, or unstructured and malformed HTML@@ -27,14 +27,10 @@ type: git location: https://github.com/ndmitchell/tagsoup.git -flag testprog- default: False- manual: True- description: Build the test program- library default-language: Haskell2010 build-depends: base == 4.*, containers, bytestring, text+ hs-source-dirs: src exposed-modules: Text.HTML.TagSoup@@ -52,19 +48,18 @@ Text.HTML.TagSoup.Specification Text.HTML.TagSoup.Type -executable test-tagsoup+test-suite test-tagsoup+ type: exitcode-stdio-1.0 default-language: Haskell2010- if flag(testprog)- buildable: True- build-depends:- base == 4.*, containers, bytestring, text,- QuickCheck >= 2.4,- deepseq >= 1.1,- time, directory, process- else- buildable: False+ build-depends:+ base == 4.*, containers, bytestring, text,+ QuickCheck >= 2.4,+ deepseq >= 1.1,+ tagsoup,+ time, directory, process main-is: Main.hs+ hs-source-dirs: test other-modules: TagSoup.Benchmark TagSoup.Sample
+ test/Main.hs view
@@ -0,0 +1,63 @@++module Main(main) where++import System.Environment+import TagSoup.Sample+import TagSoup.Test+import TagSoup.Benchmark+import Data.Char(toLower)+++helpMsg :: IO ()+helpMsg = putStr $ unlines $+ ["TagSoup, (C) Neil Mitchell 2006-2009"+ ,""+ ," tagsoup arguments"+ ,""+ ,"<url> may either be a local file, or a http[s]:// page"+ ,""+ ] ++ map f res+ where+ width = maximum $ map (length . fst) res+ res = map g actions++ g (nam,msg,Left _) = (nam,msg)+ g (nam,msg,Right _) = (nam ++ " <url>",msg)++ f (lhs,rhs) = " " ++ lhs ++ replicate (4 + width - length lhs) ' ' ++ rhs+ ++actions :: [(String, String, Either (IO ()) (String -> IO ()))]+actions = [("test","Run the test suite",Left test)+ ,("grab","Grab a web page",Right grab)+ ,("parse","Parse a web page",Right parse)+ ,("bench","Benchmark the parsing",Left time)+ ,("benchfile","Benchmark the parsing of a file",Right timefile)+ ,("validate","Validate a page",Right validate)+ ,("lastmodifieddate","Get the wiki.haskell.org last modified date",Left haskellLastModifiedDateTime)+ ,("spj","Simon Peyton Jones' papers",Left spjPapers)+ ,("ndm","Neil Mitchell's papers",Left ndmPapers)+ ,("time","Current time",Left currentTime)+ ,("google","Google Tech News",Left googleTechNews)+ ,("sequence","Creators on sequence.complete.org",Left rssCreators)+ ,("help","This help message",Left helpMsg)+ ]++main :: IO ()+main = do+ args <- getArgs+ case (args, lookup (map toLower $ head args) $ map (\(a,_,c) -> (a,c)) actions) of+ ([],_) -> do+ putStrLn "No arguments specifying, defaulting to test"+ helpMsg+ putStrLn $ replicate 70 '-'+ test+ (x:_,Nothing) -> putStrLn ("Error: unknown command " ++ x) >> helpMsg+ ([_],Just (Left a)) -> a+ (x:xs,Just (Left a)) -> do+ putStrLn $ "Warning: expected no arguments to " ++ x ++ " but got: " ++ unwords xs+ a+ ([_,y],Just (Right a)) -> a y+ (x:xs,Just (Right _)) -> do+ putStrLn $ "Error: expected exactly one argument to " ++ x ++ " but got: " ++ unwords xs+ helpMsg
+ test/TagSoup/Benchmark.hs view
@@ -0,0 +1,202 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-orphans #-} -- test file, so OK++module TagSoup.Benchmark where++import Text.HTML.TagSoup++import Control.DeepSeq+import Control.Monad+import Data.List+import Data.Maybe+import System.IO.Unsafe(unsafeInterleaveIO)+import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Lazy.Char8 as LBS+import Data.Time.Clock.POSIX(getPOSIXTime)++conf = 0.95+++timefile :: FilePath -> IO ()+timefile file = do+ -- use LBS to be most representative of real life+ lbs <- LBS.readFile file+ let str = LBS.unpack lbs+ bs = BS.concat $ LBS.toChunks lbs+ () <- LBS.length lbs `seq` length str `seq` BS.length bs `seq` return ()+ benchWith (const str, const bs, const lbs) $ benchStatic (toInteger $ LBS.length lbs)+++sample :: String+sample = "<this is a test with='attributes' and other=\"things"tested\" /><neil> is </here>" +++ "<!-- comment --> and some just random & test ><<foo></bar><bar><bob href=no>"++nsample = genericLength sample :: Integer++time :: IO ()+time = benchWith (str,bs,lbs) benchVariable+ where+ str = \i -> concat $ genericReplicate i sample+ bs = let s = BS.pack sample in \i -> BS.concat (genericReplicate i s)+ lbs = let s = LBS.pack sample in \i -> LBS.concat (genericReplicate i s)++++benchWith :: (Integer -> String, Integer -> BS.ByteString, Integer -> LBS.ByteString)+ -> ((Integer -> ()) -> IO [String]) -> IO ()+benchWith (str,bs,lbs) bench = do+ putStrLn "Timing parseTags in characters/second"+ let header = map (:[]) ["(" ++ show (round $ conf * 100) ++ "% confidence)","String","BS","LBS"]+ rows <- mapM row $ replicateM 3 [False,True]+ mapM_ (putStrLn . strict . grid) $ delay2 $ header : rows+ where+ row [a,b,c] = do+ let header = intercalate "," [g a "pos", g b "warn", g c "merge"]+ g b x = (if b then ' ' else '!') : x+ f x = bench $ \i -> rnf $ parseTagsOptions parseOptions{optTagPosition=a,optTagWarning=b,optTagTextMerge=c} $ x i+ c1 <- f str+ c2 <- f bs+ c3 <- f lbs+ return [[header],c1,c2,c3]++ strict = reverse . reverse+++---------------------------------------------------------------------+-- BENCHMARK ON THE SAMPLE INPUT++disp xs = showUnit (floor xbar) ++ " (~" ++ rng ++ "%)"+ where xbar = mean xs+ rng = if length xs <= 1 then "?" else show (ceiling $ (range conf xs) * 100 / xbar) ++cons x = fmap (x:)+++aimTime = 0.3 :: Double -- seconds to aim for+minTime = 0.2 :: Double -- below this a test is considered invalid+++-- given a number of times to repeat sample, return a list of what+-- to display+benchVariable :: (Integer -> ()) -> IO [String]+benchVariable op = cons "?" $ f 10 []+ where+ f i seen | length seen > 9 = cons (" " ++ disp seen) $ return []+ | otherwise = unsafeInterleaveIO $ do+ now <- timer $ op i+ let cps = if now == 0 then 0 else fromInteger (i * nsample) / now+ if now < minTime || (null seen && now < aimTime) then do+ let factor = min 7 $ max 2 $ floor $ aimTime / now+ cons ("? " ++ disp [cps]) $ f (i * factor) []+ else+ cons (show (9 - length seen) ++ " " ++ disp (cps:seen)) $ f i (cps:seen)++++benchStatic :: Integer -> (Integer -> ()) -> IO [String]+benchStatic nsample op = cons "?" $ f []+ where+ f seen | length seen > 9 = cons (" " ++ disp seen) $ return []+ | otherwise = unsafeInterleaveIO $ do+ now <- timer $ op $ genericLength seen+ let cps = if now == 0 then 0 else fromInteger nsample / now+ cons (show (9 - length seen) ++ " " ++ disp (cps:seen)) $ f (cps:seen)+++---------------------------------------------------------------------+-- UTILITY FUNCTIONS++-- | Given a number, show it using a unit and decimal place+showUnit :: Integer -> String+showUnit x = num ++ unit+ where+ units = " KMGTPEZY"+ (use,skip) = splitAt 3 $ show x++ unit = [units !! ((length skip + 2) `div` 3)]++ dot = ((length skip - 1) `mod` 3) + 1+ num = a ++ ['.' | b /= ""] ++ b+ where (a,b) = splitAt dot use+++-- copied from the criterion package+getTime :: IO Double+getTime = (fromRational . toRational) `fmap` getPOSIXTime++timer :: () -> IO Double+timer x = do+ start <- getTime+ () <- return x+ end <- getTime+ return $ end - start+++-- display a grid+grid :: [[String]] -> String+grid xs = unlines $ map (concat . zipWith f cols) xs+ where cols = map (maximum . map length) $ transpose xs+ f n x = x ++ replicate (n+1 - length x) ' '+++-- display a series of grids over time+-- when a grid gets to [] keep its value at that+-- when all grids get to [] return []+delay2 :: [[[String]]] -> [[[String]]]+delay2 xs = map (map head) xs : (if all (null . tail) (concat xs) then [] else delay2 $ map (map tl) xs)+ where tl (x:xs) = if null xs then x:xs else xs+++---------------------------------------------------------------------+-- INSTANCES++instance NFData a => NFData (Tag a) where+ rnf (TagOpen x y) = rnf x `seq` rnf y+ rnf (TagClose x) = rnf x+ rnf (TagText x) = rnf x+ rnf (TagComment x) = rnf x+ rnf (TagWarning x) = rnf x+ rnf (TagPosition x y) = () -- both are already ! bound+++#ifndef BYTESTRING_HAS_NFDATA+# ifdef MIN_VERSION_bytestring+# define BYTESTRING_HAS_NFDATA (MIN_VERSION_bytestring(0,10,0))+# else+# define BYTESTRING_HAS_NFDATA (__GLASGOW_HASKELL__ >= 706)+# endif+#endif++#if !BYTESTRING_HAS_NFDATA+instance NFData LBS.ByteString where+ rnf x = LBS.length x `seq` ()++instance NFData BS.ByteString where+ rnf x = BS.length x `seq` ()+#endif+++---------------------------------------------------------------------+-- STATISTICS+-- Provided by Emily Mitchell++confNs = let (*) = (,) in+ [0.95 * 1.96+ ,0.90 * 1.644]++size :: [Double] -> Double+size = genericLength++mean :: [Double] -> Double+mean xs = sum xs / size xs++stddev :: [Double] -> Double+stddev xs = sqrt $ sum [sqr (x - xbar) | x <- xs] / size xs+ where xbar = mean xs+ sqr x = x * x++-- given a sample, and a required confidence+-- of the mean (i.e. 2.5% = 0.025)+range ::Double -> [Double] -> Double+range conf xs = conf2 * stddev xs / sqrt (size xs)+ where conf2 = fromMaybe (error $ "Unknown confidence interval: " ++ show conf) $ lookup conf confNs
+ test/TagSoup/Sample.hs view
@@ -0,0 +1,158 @@++module TagSoup.Sample where++import Text.HTML.TagSoup++import Control.Exception+import Control.Monad+import Data.List+import System.Process+import System.Directory+import System.Exit+import System.IO+import Data.Functor+import Prelude+++openItem :: String -> IO String+openItem url+ | not $ "http://" `isPrefixOf` url || "https://" `isPrefixOf` url =+ readFile url+openItem url = bracket+ (openTempFile "." "tagsoup.tmp")+ (\(file,hndl) -> removeFile file)+ $ \(file,hndl) -> do+ hClose hndl+ putStrLn $ "Downloading: " ++ url+ res <- system $ "wget " ++ url ++ " -O " ++ file+ when (res /= ExitSuccess) $ error $ "Failed to download using wget: " ++ url+ src <- readFile file+ length src `seq` return src+++grab :: String -> IO ()+grab x = openItem x >>= putStr++parse :: String -> IO ()+parse x = openItem x >>= putStr . show2 . parseTags+ where+ show2 [] = "[]"+ show2 xs = "[" ++ concat (intersperseNotBroken "\n," $ map show xs) ++ "\n]\n"+++-- the standard intersperse has a strictness bug which sucks!+intersperseNotBroken :: a -> [a] -> [a]+intersperseNotBroken _ [] = []+intersperseNotBroken sep (x:xs) = x : is xs+ where+ is [] = []+ is (y:ys) = sep : y : is ys+++{-+<li id="lastmod"> This page was last modified on 9 September 2013, at 22:38.</li>+-}+haskellLastModifiedDateTime :: IO ()+haskellLastModifiedDateTime = do+ src <- openItem "http://wiki.haskell.org/Haskell"+ let lastModifiedDateTime = fromFooter $ parseTags src+ putStrLn $ "wiki.haskell.org was last modified on " ++ lastModifiedDateTime+ where fromFooter = unwords . drop 6 . words . innerText . take 2 . dropWhile (~/= "<li id=lastmod>")+++googleTechNews :: IO ()+googleTechNews = do+ tags <- fmap parseTags $ openItem "http://news.google.com/?ned=us&topic=t"+ let links = [ ascii name ++ " <" ++ maybe "unknown" shortUrl (lookup "href" atts) ++ ">"+ | TagOpen "h2" [("class","title")]:TagText spaces:TagOpen "a" atts:TagText name:_ <- tails tags]+ putStr $ unlines links+ where+ shortUrl x | "http://" `isPrefixOf` x = shortUrl $ drop 7 x+ | "www." `isPrefixOf` x = shortUrl $ drop 4 x+ | otherwise = takeWhile (/= '/') x++ ascii ('\226':'\128':'\147':xs) = '-' : ascii xs+ ascii ('\194':'\163':xs) = "#GBP " ++ ascii xs+ ascii (x:xs) = x : ascii xs+ ascii [] = []+++spjPapers :: IO ()+spjPapers = do+ tags <- parseTags <$> openItem "http://research.microsoft.com/en-us/people/simonpj/"+ let links = map f $ sections (~== "<A>") $+ takeWhile (~/= "<a name=haskell>") $+ drop 5 $ dropWhile (~/= "<a name=current>") tags+ putStr $ unlines links+ where+ f :: [Tag String] -> String+ f = dequote . unwords . words . fromTagText . head . filter isTagText++ dequote ('\"':xs) | last xs == '\"' = init xs+ dequote x = x+++ndmPapers :: IO ()+ndmPapers = do+ tags <- parseTags <$> openItem "http://community.haskell.org/~ndm/downloads/"+ let papers = map f $ sections (~== "<li class=paper>") tags+ putStr $ unlines papers+ where+ f :: [Tag String] -> String+ f xs = fromTagText (xs !! 2)+++currentTime :: IO ()+currentTime = do+ tags <- parseTags <$> openItem "http://www.timeanddate.com/worldclock/uk/london"+ let time = fromTagText (dropWhile (~/= "<span id=ct>") tags !! 1)+ putStrLn time++++type Section = String+data Package = Package {name :: String, desc :: String, href :: String}+ deriving Show++hackage :: IO [(Section,[Package])]+hackage = do+ tags <- fmap parseTags $ openItem "http://hackage.haskell.org/packages/archive/pkg-list.html"+ return $ map parseSect $ partitions (~== "<h3>") tags+ where+ parseSect xs = (nam, packs)+ where+ nam = fromTagText $ xs !! 2+ packs = map parsePackage $ partitions (~== "<li>") xs++ parsePackage xs =+ Package+ (fromTagText $ xs !! 2)+ (drop 2 $ dropWhile (/= ':') $ fromTagText $ xs !! 4)+ (fromAttrib "href" $ xs !! 1)++-- rssCreators Example: prints names of story contributors on+-- sequence.complete.org. This content is RSS (not HTML), and the selected+-- tag uses a different XML namespace "dc:creator".+rssCreators :: IO ()+rssCreators = do+ tags <- fmap parseTags $ openItem "http://sequence.complete.org/node/feed"+ putStrLn $ unlines $ map names $ partitions (~== "<dc:creator>") tags+ where names xs = fromTagText $ xs !! 1+++validate :: String -> IO ()+validate x = putStr . unlines . g . f . parseTagsOptions opts =<< openItem x+ where+ opts = parseOptions{optTagPosition=True, optTagWarning=True}++ f :: [Tag String] -> [String]+ f (TagPosition row col:TagWarning warn:rest) =+ ("Warning (" ++ show row ++ "," ++ show col ++ "): " ++ warn) : f rest+ f (TagWarning warn:rest) =+ ("Warning (?,?): " ++ warn) : f rest+ f (_:rest) = f rest+ f [] = []++ g xs = xs ++ [if n == 0 then "Success, no warnings"+ else "Failed, " ++ show n ++ " warning" ++ ['s'|n>1]]+ where n = length xs
+ test/TagSoup/Test.hs view
@@ -0,0 +1,257 @@+{-# OPTIONS_GHC -fno-warn-deprecations #-}++module TagSoup.Test(test) where++import Text.HTML.TagSoup+import Text.HTML.TagSoup.Entity+import Text.HTML.TagSoup.Match++import Control.Monad+import Data.List+import Test.QuickCheck(Arbitrary(..), Testable(..), quickCheckWithResult, stdArgs,+ Args(..), listOf, elements, Result(..))++-- * The Test Monad++type Test a = IO a++pass :: Test ()+pass = return ()++runTest :: Test () -> IO ()+runTest x = x >> putStrLn "All tests passed"++(===) :: (Show a, Eq a) => a -> a -> IO ()+a === b = if a == b then pass else fail $ "Does not equal: " ++ show a ++ " =/= " ++ show b++check :: Testable prop => prop -> IO ()+check prop = do+ res <- quickCheckWithResult stdArgs{maxSuccess=1000} prop+ case res of+ Success{} -> pass+ _ -> fail "Property failed"++newtype HTML = HTML String deriving Show+instance Arbitrary HTML where+ arbitrary = fmap (HTML . concat) $ listOf $ elements frags+ where frags = map (:[]) " \n!-</>#&;xy01[]?'\"" ++ ["CDATA","amp","gt","lt"]+ shrink (HTML x) = map HTML $ zipWith (++) (inits x) (tail $ tails x)+++-- * The Main section++test :: IO ()+test = runTest $ do+ warnTests+ parseTests+ optionsTests+ renderTests+ combiTests+ positionTests+ entityTests+ lazyTags == lazyTags `seq` pass+ matchCombinators+++{- |+This routine tests the laziness of the TagSoup parser.+For each critical part of the parser we provide a test input+with a token of infinite size.+Then the output must be infinite too.+If the laziness is broken, then the output will stop early.+We collect the thousandth character of the output of each test case.+If computation of the list stops somewhere,+you have found a laziness stopper.+-}+++lazyTags :: [Char]+lazyTags = map ((!!1000) . show . parseTags)+ [cycle "Rhabarber"+ ,repeat '&'+ ,"<"++cycle "html"+ ,"<html "++cycle "na!me=value "+ ,"<html name="++cycle "value"+ ,"<html name=\""++cycle "value"+ ,"<html name="++cycle "val!ue"+ ,"<html "++cycle "name"+ ,"</"++cycle "html"+ ,"<!-- "++cycle "comment"+ ,"<!"++cycle "doctype"+ ,"<!DOCTYPE"++cycle " description"+ ,cycle "1<2 "+ ,"&" ++ cycle "t"+ ,"<html name="++cycle "val&ue"+ ,"<html name="++cycle "va&l!ue"+ ,cycle "& test"++ -- i don't see how this can work unless the junk gets into the AST?+ -- ,("</html "++cycle "junk") :+ ]++++matchCombinators :: Test ()+matchCombinators = do+ tagText (const True) (TagText "test") === True+ tagText ("test"==) (TagText "test") === True+ tagText ("soup"/=) (TagText "test") === True+ tagOpenNameLit "table" (TagOpen "table" [("id", "name")]) === True+ tagOpenLit "table" (anyAttrLit ("id", "name")) (TagOpen "table" [("id", "name")]) === True+ tagOpenLit "table" (anyAttrNameLit "id") (TagOpen "table" [("id", "name")]) === True+ tagOpenLit "table" (anyAttrLit ("id", "name")) (TagOpen "table" [("id", "other name")]) === False+++parseTests :: Test ()+parseTests = do+ parseTags "<!DOCTYPE TEST>" === [TagOpen "!DOCTYPE" [("TEST","")]]+ parseTags "<test \"foo bar\">" === [TagOpen "test" [("\"foo",""),("bar\"","")]]+ parseTags "<test baz \"foo\">" === [TagOpen "test" [("baz",""),("\"foo\"","")]]+ parseTags "<test 'foo bar'>" === [TagOpen "test" [("'foo",""),("bar'","")]]+ parseTags "<test bar=''' />" === [TagOpen "test" [("bar",""),("'","")], TagClose "test"]+ parseTags "<test2 a b>" === [TagOpen "test2" [("a",""),("b","")]]+ parseTags "<test2 ''>" === [TagOpen "test2" [("''","")]]+ parseTags "</test foo>" === [TagClose "test"]+ parseTags "<test/>" === [TagOpen "test" [], TagClose "test"]+ parseTags "<test1 a = b>" === [TagOpen "test1" [("a","b")]]+ parseTags "hello & world" === [TagText "hello & world"]+ parseTags "hello @ world" === [TagText "hello @ world"]+ parseTags "hello @ world" === [TagText "hello @ world"]+ parseTags "hello @ world" === [TagText "hello @ world"]+ parseTags "hello &haskell; world" === [TagText "hello &haskell; world"]+ parseTags "hello \n\t world" === [TagText "hello \n\t world"]+ parseTags "<a href=http://www.google.com>" === [TagOpen "a" [("href","http://www.google.com")]]+ parseTags "<foo bar=\"bar6baz\">" === [TagOpen "foo" [("bar","bar6baz")]]+ parseTags "<foo bar=\"bar&baz\">" === [TagOpen "foo" [("bar","bar&baz")]]+ parseTags "hey &how are you" === [TagText "hey &how are you"]+ parseTags "hey &how; are you" === [TagText "hey &how; are you"]+ parseTags "hey & are you" === [TagText "hey & are you"]+ parseTags "hey & are you" === [TagText "hey & are you"]++ -- real cases reported by users+ parseTags "↖x≧̸" === [TagText ['\x2196','x','\x2267','\x0338']]+ parseTags "test � test" === [TagText "test ? test"]++ parseTags "<a href=\"series.php?view=single&ID=72710\">" === [TagOpen "a" [("href","series.php?view=single&ID=72710")]]++ parseTags "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" ===+ [TagOpen "!DOCTYPE" [("HTML",""),("PUBLIC",""),("","-//W3C//DTD HTML 4.01//EN"),("","http://www.w3.org/TR/html4/strict.dtd")]]++ parseTags "<script src=\"http://edge.jobthread.com/feeds/jobroll/?s_user_id=100540&subtype=slashdot\">" ===+ [TagOpen "script" [("src","http://edge.jobthread.com/feeds/jobroll/?s_user_id=100540&subtype=slashdot")]]++ parseTags "<a title='foo'bar' href=correct>text" === [TagOpen "a" [("title","foo"),("bar'",""),("href", "correct")],TagText "text"]++ parseTags "<test><![CDATA[Anything goes, <em>even hidden markup</em> & entities]]> but this is outside</test>" ===+ [TagOpen "test" [],TagText "Anything goes, <em>even hidden markup</em> & entities but this is outside",TagClose "test"]++ parseTags "<a \r\n href=\"url\">" === [TagOpen "a" [("href","url")]]++ parseTags "<a href='random.php'><img src='strips/130307.jpg' alt='nukular bish'' title='' /></a>" === + [TagOpen "a" [("href","random.php")],TagOpen "img" [("src","strips/130307.jpg"),("alt","nukular bish"),("'",""),("title","")],TagClose "img",TagClose "a"]++ parseTags "<p>some text</p\n<img alt='< &yyy; >' src=\"abc.gif\">" ===+ [TagOpen "p" [],TagText "some text",TagClose "p"]++ parseTags "<script> if (x<bomb) </script>" === [TagOpen "script" [], TagText " if (x<bomb) ", TagClose "script"]+ parseTags "<script> if (x<bomb) " === [TagOpen "script" [], TagText " if (x<bomb) "]+ parseTags "<SCRIPT language=foo> if (x<bomb) </SCRIPT>" === [TagOpen "SCRIPT" [("language","foo")], TagText " if (x<bomb) ", TagClose "SCRIPT"]+ parseTags "<script /><test>" === [TagOpen "script" [], TagClose "script", TagOpen "test" []]++ -- some escapes require trailing semicolons, see #28 and #27.+ parseTags "one ∣ two" === [TagText "one \8739 two"]+ parseTags "one &mid two" === [TagText "one &mid two"]+ parseTags "one µ two" === [TagText "one \181 two"]+ parseTags "one µ two" === [TagText "one \181 two"]++optionsTests :: Test ()+optionsTests = check $ \(HTML x) -> all (f x) $ replicateM 3 [False,True]+ where+ f str [pos,warn,merge] =+ bool "merge" (not merge || adjacentTagText tags) &&+ bool "warn" (warn || all (not . isTagWarning) tags) &&+ bool "pos" (if pos then alternatePos tags else all (not . isTagPosition) tags)+ where tags = parseTagsOptions parseOptions{optTagPosition=pos,optTagWarning=warn,optTagTextMerge=merge} str+ bool x b = b || error ("optionsTests failed with " ++ x ++ " on " ++ show (pos,warn,merge,str,tags))++ -- optTagTextMerge implies no adjacent TagText cells+ -- and none separated by only warnings or positions+ adjacentTagText = g True -- can the next be a tag text+ where g i (x:xs) | isTagText x = i && g False xs+ | isTagPosition x || isTagWarning x = g i xs+ | otherwise = g True xs+ g i [] = True++ -- optTagPosition implies every element must be followed+ -- by a position node, no two position nodes must be adjacent+ -- and all positions must be increasing+ alternatePos (TagPosition l1 c1 : x : TagPosition l2 c2 : xs)+ | (l1,c1) <= (l2,c2) && not (isTagPosition x) = alternatePos $ TagPosition l2 c2 : xs+ alternatePos [TagPosition l1 c1, x] | not $ isTagPosition x = True+ alternatePos [] = True+ alternatePos _ = False+++renderTests :: Test ()+renderTests = do+ let rp = renderTags . parseTags+ rp "<test>" === "<test>"+ rp "<br></br>" === "<br />"+ rp "<script></script>" === "<script></script>"+ rp "hello & world" === "hello & world"+ rp "<a href=test>" === "<a href=\"test\">"+ rp "<a href>" === "<a href>"+ rp "<a href?>" === "<a href?>"+ rp "<?xml foo?>" === "<?xml foo ?>"+ rp "<?xml foo?>" === "<?xml foo ?>"+ rp "<!-- neil -->" === "<!-- neil -->"+ rp "<a test=\"a'b\">" === "<a test=\"a'b\">"+ escapeHTML "this is a &\" <test> '" === "this is a &" <test> '"+ check $ \(HTML x) -> let y = rp x in rp y == (y :: String)++ +entityTests :: Test ()+entityTests = do+ lookupNumericEntity "65" === Just "A"+ lookupNumericEntity "x41" === Just "A"+ lookupNumericEntity "x4E" === Just "N"+ lookupNumericEntity "x4e" === Just "N"+ lookupNumericEntity "X4e" === Just "N"+ lookupNumericEntity "Haskell" === Nothing+ lookupNumericEntity "" === Nothing+ lookupNumericEntity "89439085908539082" === Nothing+ lookupNamedEntity "amp" === Just "&"+ lookupNamedEntity "haskell" === Nothing+ escapeXML "hello world" === "hello world"+ escapeXML "hello & world" === "hello & world"+++combiTests :: Test ()+combiTests = do+ (TagText "test" ~== TagText "" ) === True+ (TagText "test" ~== TagText "test") === True+ (TagText "test" ~== TagText "soup") === False+ (TagText "test" ~== "test") === True+ (TagOpen "test" [] ~== "<test>") === True+ (TagOpen "test" [] ~== "<soup>") === False+ (TagOpen "test" [] ~/= "<soup>") === True+ (TagComment "foo" ~== "<!--foo-->") === True+ (TagComment "bar" ~== "<!--bar-->") === True+++positionTests :: Test ()+positionTests = do+ let p = parseTagsOptions parseOptions{optTagPosition=True,optTagWarning=False}+ p "<a>&</a>" === [TagPosition 1 1,TagOpen "a" [],TagPosition 1 4,TagText "&",TagPosition 1 5,TagClose "a"]+ p "<a>&#z" === [TagPosition 1 1,TagOpen "a" [],TagPosition 1 4,TagText "&#z"]+ p "<a>&xz" === [TagPosition 1 1,TagOpen "a" [],TagPosition 1 4,TagText "&xz"]+ p "<a>&" === [TagPosition 1 1,TagOpen "a" [],TagPosition 1 4,TagText "&"]+ p "<a>&1" === [TagPosition 1 1,TagOpen "a" [],TagPosition 1 4,TagText "&1"]+ p "<a>&" === [TagPosition 1 1,TagOpen "a" [],TagPosition 1 4,TagText "&"]+++warnTests :: Test ()+warnTests = do+ let p = parseTagsOptions parseOptions{optTagPosition=True,optTagWarning=True}+ wt x = [(msg,c) | TagWarning msg:TagPosition _ c:_ <- tails $ p x]+ wt "neil &foo bar" === [("Unknown entity: foo",6)]