HaTeX 3.12.0.0 → 3.13.0.0
raw patch · 5 files changed
+131/−70 lines, 5 filesdep +parsecdep −attoparsecPVP ok
version bump matches the API change (PVP)
Dependencies added: parsec
Dependencies removed: attoparsec
API changes (from Hackage documentation)
- Text.LaTeX.Base.Parser: latexAtOnce :: Text -> Either String LaTeX
+ Text.LaTeX.Base.Parser: Expect :: !String -> Message
+ Text.LaTeX.Base.Parser: Message :: !String -> Message
+ Text.LaTeX.Base.Parser: SysUnExpect :: !String -> Message
+ Text.LaTeX.Base.Parser: UnExpect :: !String -> Message
+ Text.LaTeX.Base.Parser: data Message :: *
+ Text.LaTeX.Base.Parser: data ParseError :: *
+ Text.LaTeX.Base.Parser: data SourcePos :: *
+ Text.LaTeX.Base.Parser: errorMessages :: ParseError -> [Message]
+ Text.LaTeX.Base.Parser: errorPos :: ParseError -> SourcePos
+ Text.LaTeX.Base.Parser: messageString :: Message -> String
+ Text.LaTeX.Base.Parser: parseLaTeXFile :: FilePath -> IO (Either ParseError LaTeX)
+ Text.LaTeX.Base.Parser: sourceColumn :: SourcePos -> Column
+ Text.LaTeX.Base.Parser: sourceLine :: SourcePos -> Line
+ Text.LaTeX.Base.Parser: sourceName :: SourcePos -> SourceName
- Text.LaTeX.Base.Parser: parseLaTeX :: Text -> Either String LaTeX
+ Text.LaTeX.Base.Parser: parseLaTeX :: Text -> Either ParseError LaTeX
Files
- HaTeX.cabal +7/−5
- Text/LaTeX/Base/Parser.hs +116/−58
- Text/LaTeX/Base/Syntax.hs +1/−1
- parsertest/parsertest.hs +2/−2
- test/Main.hs +5/−4
HaTeX.cabal view
@@ -1,5 +1,5 @@ Name: HaTeX -Version: 3.12.0.0 +Version: 3.13.0.0 Author: Daniel Díaz Category: Text, LaTeX Build-type: Simple @@ -45,13 +45,15 @@ Default-language: Haskell2010 Build-depends: base == 4.* , bytestring >= 0.9.2.1 && < 0.11 - , transformers >= 0.2.2 && < 0.5 , text >= 0.11.2.3 && < 2 - , attoparsec >= 0.10.2 && < 0.12 - , matrix + , transformers >= 0.2.2 && < 0.5 , containers >= 0.4.2.1 && < 0.6 + , matrix + -- Testing , QuickCheck - -- For pretty-printing + -- Parsing + , parsec >= 3.1 + -- Pretty-printing , wl-pprint-extras >= 3.5 Exposed-modules: Text.LaTeX
Text/LaTeX/Base/Parser.hs view
@@ -1,67 +1,84 @@-{-# LANGUAGE CPP #-}+ {-# LANGUAGE OverloadedStrings #-}----------------------------------------------------------------------------------- |--- Module : Text/LaTeX/Base/Parser.hs--- Copyright : (c) Tobias Schoofs--- License : LGPL --- Stability : experimental--- Portability: portable++-- | The /LaTeX/ parser.+-- +-- Use 'parseLaTeX' to parse a 'Text' containing /LaTeX/ code.+-- If the 'Text' is in a file, you may want to use 'parseLaTeXFile'.+-- Use this module together with "Text.LaTeX.Base.Syntax" to perform+-- analysis and transformations of /LaTeX/ code. The parser ('parseLaTeX')+-- is related with the renderer ('render') by the following property: ----- LaTeX Parser based on Attoparsec--------------------------------------------------------------------------------+-- /If @t :: Text@ is a syntactically valid LaTeX block, then:/+--+-- > fmap render (parseLaTeX t) == Right t+-- +-- This property says two things:+--+-- * Given a valid LaTeX input, 'parseLaTeX' returns a 'LaTeX' value.+-- * If the parsed value is again rendered, you get the initial input.+--+-- In other words, 'parseLaTeX' is a partial function defined over the+-- set of valid LaTeX files, and 'render' is its inverse.+-- module Text.LaTeX.Base.Parser (+ -- * The parser parseLaTeX+ , parseLaTeXFile , latexParser , latexBlockParser- , latexAtOnce-#ifdef _TEST- , specials-#endif+ -- * Parsing errors+ , ParseError+ , errorPos+ , errorMessages+ -- ** Error messages+ , Message (..)+ , messageString+ -- ** Source positions+ , SourcePos+ , sourceLine+ , sourceColumn+ , sourceName ) where -import Data.Attoparsec.Text hiding (take, takeTill)-import qualified Data.Attoparsec.Text as A (takeTill)-import Data.Char (toLower)+import Text.Parsec hiding ((<|>),many)+import Text.Parsec.Text+import Text.Parsec.Error+import Data.Char (toLower,digitToInt) import Data.Monoid import Data.Maybe (fromMaybe)-import Data.Text (Text) import qualified Data.Text as T -import Control.Applicative ((<|>), (<$>),many)+import Control.Applicative import Control.Monad (unless) import Text.LaTeX.Base.Syntax+import Text.LaTeX.Base.Render -- | Parse a 'Text' sequence as a 'LaTeX' block. If it fails, it returns -- an error string.-parseLaTeX :: Text -> Either String LaTeX+parseLaTeX :: Text -> Either ParseError LaTeX parseLaTeX t | T.null t = return TeXEmpty- | otherwise = - case parse latexParser t of- Fail _ _ e -> Left e- Done _ r -> Right r- rx@(Partial _) -> -- Left "incomplete input"- case feed rx T.empty of- Fail _ _ e -> Left e- Partial _ -> Left "incomplete input"- Done _ r -> Right r--{-# DEPRECATED latexAtOnce "Use parseLaTeX instead." #-}+ | otherwise = parse latexParser "parseLaTeX input" t --- | Same as 'parseLaTeX'.-latexAtOnce :: Text -> Either String LaTeX-latexAtOnce = parseLaTeX+-- | Read a file and parse it as 'LaTeX'.+parseLaTeXFile :: FilePath -> IO (Either ParseError LaTeX)+parseLaTeXFile fp = parse latexParser fp <$> readFileTex fp ---------------------------------------------------------------------------- | Incremental 'LaTeX' parser.-------------------------------------------------------------------------+-- | The 'LaTeX' parser. latexParser :: Parser LaTeX-latexParser = mconcat <$> latexBlockParser `manyTill` endOfInput +latexParser = mconcat <$> latexBlockParser `manyTill` eof -- | Parser of a single 'LaTeX' constructor, no appending blocks. latexBlockParser :: Parser LaTeX-latexBlockParser = foldr1 (<|>) [text, dolMath, comment, text2, environment, command]+latexBlockParser = foldr1 (<|>)+ [ text <?> "text"+ , dolMath <?> "inline math ($)"+ , comment <?> "comment"+ , text2 <?> "text2"+ , try environment <?> "environment"+ , command <?> "command"+ ] -- Note: text stops on ']'; if the other parsers fail on the rest -- text2 handles it, starting with ']' @@ -74,7 +91,7 @@ case mbC of Nothing -> fail "text: Empty input." Just c | c `elem` "$%\\{]}" -> fail "not text"- | otherwise -> TeXRaw <$> A.takeTill (`elem` "$%\\{]}")+ | otherwise -> TeXRaw <$> takeTill (`elem` "$%\\{]}") ------------------------------------------------------------------------ -- Text without stopping on ']'@@ -92,41 +109,43 @@ environment = anonym <|> env anonym :: Parser LaTeX-anonym = char '{' >> - TeXBraces . mconcat <$> latexBlockParser `manyTill` char '}'+anonym = do+ _ <- char '{'+ l <- TeXBraces . mconcat <$> many latexBlockParser+ _ <- char '}'+ return l env :: Parser LaTeX env = do- _ <- char '\\'- n <- envName "begin"+ n <- char '\\' *> envName "begin" sps <- many $ char ' ' let lsps = if null sps then mempty else TeXRaw $ T.pack sps as <- cmdArgs b <- envBody n - return $ TeXEnv (T.unpack n) (fromMaybe [] as) $+ return $ TeXEnv n (fromMaybe [] as) $ case as of Just [] -> lsps <> TeXBraces mempty <> b Nothing -> lsps <> b _ -> b -envName :: Text -> Parser Text+envName :: String -> Parser String envName k = do _ <- string k _ <- char '{'- n <- A.takeTill (== '}')+ n <- takeTill (== '}') _ <- char '}'- return n+ return $ T.unpack n -envBody :: Text -> Parser LaTeX+envBody :: String -> Parser LaTeX envBody n = mconcat <$> (bodyBlock n) `manyTill` endenv- where endenv = try $ string ("\\end") >> skipSpace >> string ("{" <> n <> "}")+ where endenv = try $ string ("\\end") >> spaces >> string ("{" <> n <> "}") -bodyBlock :: Text -> Parser LaTeX+bodyBlock :: String -> Parser LaTeX bodyBlock n = do c <- peekChar case c of Just _ -> latexBlockParser- _ -> fail $ "Environment '" <> T.unpack n <> "' not finalized."+ _ -> fail $ "Environment '" <> n <> "' not finalized." ------------------------------------------------------------------------ -- Command@@ -140,7 +159,7 @@ Just x -> if isSpecial x then special else do- c <- A.takeTill endCmd+ c <- takeTill endCmd -- if c `elem` ["begin","end"] -- then fail $ "Command not allowed: " ++ T.unpack c -- else maybe (TeXCommS $ T.unpack c) (TeXComm $ T.unpack c) <$> cmdArgs@@ -161,7 +180,7 @@ '[' -> "]" '{' -> "}" _ -> error "this cannot happen!"- b <- mconcat <$> latexBlockParser `manyTill` string e+ b <- mconcat <$> manyTill latexBlockParser (string e) case c of '[' -> return $ OptArg b '{' -> return $ FixArg b@@ -185,6 +204,7 @@ ------------------------------------------------------------------------ -- Line break ------------------------------------------------------------------------+ lbreak :: Parser LaTeX lbreak = do y <- try (char '[' <|> char '*' <|> return ' ') @@ -197,13 +217,13 @@ _ -> return (TeXLineBreak Nothing False) linebreak :: Bool -> Parser LaTeX-linebreak t = do m <- measure+linebreak t = do m <- measure <?> "measure" _ <- char ']' s <- try (char '*' <|> return ' ') return $ TeXLineBreak (Just m) (t || s == '*') measure :: Parser Measure-measure = try (double >>= unit) <|> CustomMeasure <$> latexBlockParser+measure = try (floating >>= unit) <|> (CustomMeasure . mconcat) <$> manyTill latexBlockParser (lookAhead $ char ']') unit :: Double -> Parser Measure unit f = do@@ -241,7 +261,7 @@ b <- mconcat <$> latexBlockParser `manyTill` char '$' return $ TeXMath Dollar b -math :: MathType -> Text -> Parser LaTeX+math :: MathType -> String -> Parser LaTeX math t eMath = do b <- mconcat <$> latexBlockParser `manyTill` try (string eMath) return $ TeXMath t b@@ -252,7 +272,7 @@ comment :: Parser LaTeX comment = do _ <- char '%'- c <- A.takeTill (== '\n')+ c <- takeTill (== '\n') e <- atEnd unless e (char '\n' >>= \_ -> return ()) return $ TeXComment c@@ -260,6 +280,7 @@ ------------------------------------------------------------------------ -- Helpers ------------------------------------------------------------------------+ isSpecial :: Char -> Bool isSpecial = (`elem` specials) @@ -272,3 +293,40 @@ specials :: String specials = "'(),.-\"!^$&#{}%~|/:;=[]\\` "++peekChar :: Parser (Maybe Char)+peekChar = Just <$> (try $ lookAhead anyChar) <|> pure Nothing++atEnd :: Parser Bool+atEnd = (eof *> pure True) <|> pure False++takeTill :: (Char -> Bool) -> Parser Text+takeTill p = T.pack <$> many (satisfy (not . p))++-- Parsing doubles+--+-- Code for 'floating', 'fractExponent', and 'sign' comes from parsers package:+--+-- http://hackage.haskell.org/package/parsers+--++floating :: Parser Double+floating = decimal <**> fractExponent++fractExponent :: Parser (Integer -> Double)+fractExponent = (\fract expo n -> (fromInteger n + fract) * expo) <$> fraction <*> option 1.0 exponent'+ <|> (\expo n -> fromInteger n * expo) <$> exponent' where+ fraction = foldr op 0.0 <$> (char '.' *> (some digit <?> "fraction"))+ op d f = (f + fromIntegral (digitToInt d))/10.0+ exponent' = ((\f e -> power (f e)) <$ oneOf "eE" <*> sign <*> (decimal <?> "exponent")) <?> "exponent"+ power e+ | e < 0 = 1.0/power(-e)+ | otherwise = fromInteger (10^e)++decimal :: Parser Integer+decimal = read <$> many1 digit++sign :: Parser (Integer -> Integer)+sign = negate <$ char '-'+ <|> id <$ char '+'+ <|> pure id
Text/LaTeX/Base/Syntax.hs view
@@ -256,7 +256,7 @@ arbitraryChar = elements $ ['A'..'Z'] ++ ['a'..'z'] - ++ "\n-+*/!\"$%&()[]{}^_.,:;'#@<>?\\ " + ++ "\n-+*/!\"$%&(){}^_.,:;'#@<>?\\ " -- | Utility for the instance of 'LaTeX' to 'Arbitrary'. -- We generate a short sequence of characters and
parsertest/parsertest.hs view
@@ -12,7 +12,7 @@ t <- T.readFile $ "example" ++ show i ++ ".tex" case parseLaTeX t of Left err -> do putStrLn "Failed."- putStrLn $ "The error was: " ++ err+ putStrLn $ "The error was: " ++ show err return False Right _ -> putStrLn "Succeed." >> return True @@ -23,4 +23,4 @@ let b = and bs putStrLn $ "Parser Test Passed: " ++ show b if b then return ()- else putStrLn $ "Number of errors: " ++ show (length $ filter (==False) bs)+ else putStrLn $ "Test result: " ++ show (length $ filter (==True) bs) ++ "/" ++ show (length bs)
test/Main.hs view
@@ -6,6 +6,9 @@ import qualified Test.Tasty.QuickCheck as QC import Test.QuickCheck +instance Eq ParseError where+ _ == _ = undefined+ main :: IO () main = defaultMain $ testGroup "HaTeX" [ testGroup "LaTeX"@@ -17,9 +20,7 @@ ] , testGroup "Parser" [ QC.testProperty "render . parse = id" $- \l0 -> let t = render (l0 :: LaTeX)- in case parseLaTeX t of- Left _ -> False- Right l -> render l == t+ \l -> let t = render (l :: LaTeX)+ in fmap render (parseLaTeX t) == Right t ] ]