bibtex 0.0.3 → 0.0.4
raw patch · 5 files changed
+355/−49 lines, 5 filesdep +containersdep +utf8-stringPVP ok
version bump matches the API change (PVP)
Dependencies added: containers, utf8-string
API changes (from Hackage documentation)
Files
- Makefile +5/−4
- bibtex.cabal +20/−2
- src/Hackage.hs +102/−36
- src/Text/BibTeX/Format.hs +1/−1
- src/Text/LaTeX/Character.hs +227/−6
Makefile view
@@ -14,7 +14,8 @@ (cd $(dir $<); pdflatex $(notdir $<); pdflatex $(notdir $<)) %-cite.tex: %.bib- ghc -e main src/Publications.hs < $< >$@+ ./dist/build/publication-overview/publication-overview < $< >$@+# ghc -e main src/Publications.hs < $< >$@ %.bbl: %.aux tex/publications.bib (cd $(dir $<); bibtex $(notdir $*))@@ -25,6 +26,6 @@ hackbib: hackage.bib -hackage.bib: $(HOME)/.cabal/packages/hackage.haskell.org/00-index.tar.gz- gunzip --stdout $< | ghc -e main src/Hackage.hs >$@-# gunzip --stdout $< | hackage-bibtex >$@+hackage.bib: $(HOME)/.cabal/packages/hackage.haskell.org/00-index.tar.gz src/Hackage.hs+ gunzip --stdout $< | ./dist/build/hackage-bibtex/hackage-bibtex >$@+# gunzip --stdout $< | ghc -e main src/Hackage.hs >$@
bibtex.cabal view
@@ -1,5 +1,5 @@ Name: bibtex-Version: 0.0.3+Version: 0.0.4 License: BSD3 License-File: LICENSE Author: Henning Thielemann <haskell@henning-thielemann.de>@@ -44,6 +44,22 @@ The file @hackage.bib@ is written to the current directory. The program reads an uncompressed tar archive from standard input and writes the result bibliography file to standard output.+ .+ Note that @hackage.bib@ exceeds some limits of standard BibTeX and LaTeX:+ There are currently much more than 5000 versions of packages,+ the maximum my BibTeX can handle at once.+ That is, you can use the bibliography file,+ but you cannot cite all entries with @\\nocite*@.+ If there are more than 26 uploads by the same author in a year,+ the BibTeX style @alpha@ generates identifiers including curly braces+ -- like @Thi2009\{@,+ which interacts badly with LaTeX's handling of them.+ If you reduce the Bibliography file to 5000 entries+ and try to generate an overview of all entries with @\\nocite@,+ -- @\\nocite{*}@,+ then @pdflatex@ hits its limits:+ .+ > TeX capacity exceeded, sorry [save size=5000] Tested-With: GHC==6.10.4 Cabal-Version: >=1.6 Build-Type: Simple@@ -58,7 +74,7 @@ Source-Repository this type: darcs location: http://code.haskell.org/~thielema/bibtex/- tag: 0.0.3+ tag: 0.0.4 Flag base2 description: Choose the new smaller, split-up base package.@@ -70,6 +86,7 @@ Library Build-Depends: parsec >=2.1 && <3.1,+ containers >= 0.1 && <0.4, utility-ht >=0.0.5 && <0.1 If flag(base2) Build-Depends:@@ -102,6 +119,7 @@ old-time >=1.0 && <1.1, Cabal >=1.6 && <1.10, tar >=0.3 && <0.4,+ utf8-string >=0.3.4 && <0.4, bytestring >=0.9 && <0.10 Else Buildable: False
src/Hackage.hs view
@@ -1,5 +1,9 @@ module Main where +import qualified Text.BibTeX.Format as Format+import qualified Text.BibTeX.Entry as Entry+import qualified Text.LaTeX.Character as LaTeX+ import qualified Distribution.PackageDescription.Parse as PkgP import qualified Distribution.PackageDescription as PkgD import qualified Distribution.Package as Pkg@@ -17,14 +21,17 @@ import qualified Codec.Archive.Tar as Tar import qualified Codec.Archive.Tar.Entry as TarEnt+import qualified Data.ByteString.Lazy.UTF8 as UTF8 import qualified Data.ByteString.Lazy as B import qualified System.IO as IO import Distribution.Text (display, ) -import Data.List.HT (dropWhileRev, )-import Data.Char (toLower, isSpace, isAlpha, chr, )-import Data.Version (showVersion, )+import Data.String.HT (trim, )+import Data.Tuple.HT (mapFst, )+import Data.List.HT (switchL, switchR, )+import Data.Char (toLower, isSpace, isAlpha, chr, )+import Data.Version (showVersion, ) import qualified Data.List as List @@ -35,68 +42,127 @@ packageURL pkgid = "/package/" ++ display pkgid -fromPackage :: CalendarTime -> PackageDescription -> String+{- |+Filter out parts in parentheses and e-mails addresses+-}+removeAnnotations :: String -> String+removeAnnotations "" = ""+removeAnnotations (c:cs) =+ case c of+ '(' -> removeAnnotations $ drop 1 $ dropWhile (')'/=) cs+ '<' -> removeAnnotations $ drop 1 $ dropWhile ('>'/=) cs+ _ -> c : removeAnnotations cs++splitList :: String -> [String]+splitList =+ let separate rest = ([], uncurry (:) $ recourse rest)+ continue c rest = mapFst (c:) $ recourse rest+ recourse str =+ case str of+ '/' : rest -> separate rest+ '&' : rest -> separate rest+ ',' : rest -> separate rest+ c0:rest0@('a':'n':'d':c1:rest) ->+ if isSpace c0 && isSpace c1+ then separate rest+ else continue c0 rest0+ c:rest -> continue c rest+ "" -> ([], [])+ in uncurry (:) . recourse++splitAuthorList :: String -> [String]+splitAuthorList =+ map (\author ->+ case author of+ "..." -> "others"+ "et al." -> "others"+ _ -> author) .+ filter (not . null) .+ map trim .+ splitList .+ -- remove numbers, quotation marks ...+ filter (\c -> isAlpha c || isSpace c || elem c ".-'@/&,") .+ removeAnnotations++{- authors must be split with respect to ',', '/', '&' and ' and ' -}+fromPackage :: CalendarTime -> PackageDescription -> Entry.T fromPackage time pkg =- let author =- let str = dropWhile isSpace $ PkgD.author pkg- in case str of- '"' : t -> takeWhile ('"' /=) t- _ -> dropWhileRev isSpace $ takeWhile ('<' /=) str+ let authors =+ splitAuthorList $ PkgD.author pkg surname =- let nameParts = words author- in if null nameParts- then ""- else filter isAlpha $ last nameParts+ switchL "unknown" (\firstAuthor _ ->+ switchR "" (\_ -> filter isAlpha) $+ words firstAuthor) $+ authors pkgId = PkgD.package pkg Pkg.PackageName name = Pkg.pkgName pkgId year = ctYear time versionStr = showVersion (Pkg.pkgVersion pkgId)- in unlines $- ("@Misc{" ++ map toLower surname ++ show year ++- name ++ "-" ++ versionStr ++ ",") :- map (\(field,value) -> " " ++ field ++ " = {" ++ value ++ "},") (- ("author", author) :- ("title", "{" ++ name ++ ": " ++ PkgD.synopsis pkg ++ "}") :- ("howpublished",- "\\url{http://hackage.haskell.org" ++- packageURL (PkgD.package pkg) ++ "}") :- ("year", show year) :- ("month", show (ctMonth time)) :- ("version", versionStr) :- ("keywords", "Haskell, " ++ PkgD.category pkg ) :- ("subtype", "program") :- []) ++- "}" :+ bibId =+ map toLower surname ++ show year +++ name ++ "-" ++ versionStr+ in Entry.Cons "Misc" bibId $+ ("author",+ if null authors+ then "unknown"+ else Format.authorList $+ map LaTeX.fromUnicodeString authors) :+ ("title",+ "{" ++ name ++ ": " +++ LaTeX.fromUnicodeString (PkgD.synopsis pkg) ++ "}") :+ ("howpublished",+ "\\url{http://hackage.haskell.org" +++ packageURL (PkgD.package pkg) ++ "}") :+ ("year", show year) :+ ("month", show (ctMonth time)) :+ ("version", versionStr) :+ ("keywords", "Haskell, " ++ PkgD.category pkg ) :+ ("subtype", "program") : [] example :: IO () example = do now <- toCalendarTime =<< getClockTime pkg <- readPackageDescription Verbosity.silent "example.cabal"- putStrLn (fromPackage now (PkgD.packageDescription pkg))+ putStrLn (Format.entry $ fromPackage now $ PkgD.packageDescription pkg) -fromTarEntry :: Tar.Entry -> String+{- |+This decodes UTF-8 but in contrast to UTF8.toString+it handles invalid characters like Latin-1 ones.+This way we can also cope with many texts that contain actually Latin-1.+-}+decodeUTF8orLatin :: B.ByteString -> String+decodeUTF8orLatin =+ List.unfoldr (\bstr ->+ flip fmap (UTF8.uncons bstr) $ \(c, rest) ->+ if c==UTF8.replacement_char+ then (chr $ fromIntegral $ B.head bstr, B.tail bstr)+ else (c,rest))+++fromTarEntry :: Tar.Entry -> B.ByteString fromTarEntry ent = if List.isSuffixOf ".cabal" (TarEnt.entryPath ent) then case TarEnt.entryContent ent of TarEnt.NormalFile txt _size ->- case parsePackageDescription- (map (chr . fromIntegral) (B.unpack txt)) of+ UTF8.fromString $+ case parsePackageDescription (decodeUTF8orLatin txt) of PkgP.ParseOk _ pkg ->+ Format.entry $ fromPackage (toUTCTime (TOD (fromIntegral $ TarEnt.entryTime ent) 0)) (PkgD.packageDescription pkg) PkgP.ParseFailed msg -> show msg- _ -> ""- else ""+ _ -> B.empty+ else B.empty main :: IO () main = Tar.foldEntries (\entry cont ->- putStrLn (fromTarEntry entry) >> cont)+ B.putStrLn (fromTarEntry entry) >> cont) (return ()) (IO.hPutStr IO.stderr) . Tar.read =<< B.getContents
src/Text/BibTeX/Format.hs view
@@ -14,7 +14,7 @@ " "++name++" = {"++value++"},\n" in "@" ++ entryType ++ "{" ++ bibId ++ ",\n" ++ concatMap formatItem items ++- "}\n\n"+ "}\n" enumerate :: [String] -> String
src/Text/LaTeX/Character.hs view
@@ -1,17 +1,79 @@-module Text.LaTeX.Character where+module Text.LaTeX.Character (+ toUnicodeString,+ fromUnicodeString,+ table,+ ) where -import Data.List.HT (multiReplace, )-import Data.Tuple.HT (swap, )+-- import Data.List.HT (multiReplace, )+import Data.Tuple.HT (mapSnd, )+import Data.Char (isLetter, chr, ord, )+import qualified Data.Map as Map +{- |+Replace LaTeX macros for special characters+by Unicode characters in a lazy way.+-} toUnicodeString :: String -> String-toUnicodeString = multiReplace table+toUnicodeString =+ filter (not . flip elem "{}") .+ toUnicodeStringCore+{-+toUnicodeString =+ multiReplace table+-} +toUnicodeStringCore :: String -> String+toUnicodeStringCore "" = ""+toUnicodeStringCore (c:cs) =+ case c of+ '\\' ->+ let getArgument "" = ("", "")+ getArgument (a:argsuffix) =+ if a/='{'+ then ([a], argsuffix)+ else+ {-+ this does not support nested curly braces,+ however, I have no argument with braces in my dictionary+ -}+ let (argstr, suffix) = break ('}'==) argsuffix+ in (argstr, drop 1 suffix)+ translateInvocation = do+ (macro,(arg,rest)) <-+ case cs of+ [] -> Nothing+ b:bs -> Just $+ if elem b "'`^\"~"+ then ([b], getArgument bs)+ else mapSnd getArgument $+ span isLetter cs+ code <- Map.lookup (macro,arg) toMap+ return (code, rest)+ in case translateInvocation of+ Just (code,rest) -> code : toUnicodeStringCore rest+ Nothing -> c : toUnicodeStringCore cs+ '$' ->+ let (math, rest) = break ('$'==) cs+ in parseMathString math ++ toUnicodeStringCore (drop 1 rest)+ _ -> c : toUnicodeStringCore cs++parseMathString :: String -> String+parseMathString "" = ""+parseMathString (c:cs) =+ if c/='\\'+ then c : parseMathString cs+ else let (ident,rest) = span isLetter cs+ in maybe ('\\':ident) (:[])+ (Map.lookup ident mathMap) +++ parseMathString rest+ fromUnicodeString :: String -> String fromUnicodeString =- multiReplace (map swap table)-+ concatMap (\c -> Map.findWithDefault [c] c fromMap)+-- multiReplace (map swap table) +{-# DEPRECATED table "use toUnicodeString or fromUnicodeString" #-} table :: [(String, String)] table = ("\\&", "&") :@@ -27,4 +89,163 @@ ("\\'e", "é") : ("\\'a", "á") : ("\\'{\\i}", "í") :+ ("\\'u", "ú") :+ ("\\'U", "Ú") :+ ("\\o{}", "ø") :+ ("\\O{}", "Ø") :+ ("\\oe{}", "œ") :+ ("\\OE{}", "Œ") :+ ("\\ae{}", "æ") :+ ("\\AE{}", "Æ") :+ ("\\l{}", "ł") :+ ("\\L{}", "Ł") :+ ("\\c{c}", "ç") :+ ("\\c{C}", "Ç") :+ ("\\~a", "ã") :+ ("\\~A", "Ã") : []+++fromMap :: Map.Map Char String+fromMap =+ Map.fromList+ (do (base, variants) <- accents+ (accent, code) <- variants+ return (chr code, '\\':accent:'{':base:'}':""))+ `Map.union`+ Map.fromList+ (map (\(ident, code) -> (chr code, "\\"++ident++"{}")) specialChars)+ `Map.union`+ Map.fromList+ -- curly braces around dollars assert that no $$ can occur in the output+ (map (\(ident, code) -> (chr code, "{$\\"++ident++"$}")) mathChars)+ `Map.union`+ Map.fromList+ (map (\c -> (c, '\\':c:[])) escapedChars)+++toMap :: Map.Map (String, String) Char+toMap =+ Map.fromList+ (do (base, variants) <- accents+ (accent, code) <- variants+ return (([accent], [base]), chr code))+ `Map.union`+ Map.fromList+ (map (\(ident, code) -> ((ident, ""), chr code)) specialChars)+ `Map.union`+ Map.fromList+ (map (\c -> (('\\':c:[], ""), c)) escapedChars)+++accents :: [(Char, [(Char, Int)])]+accents =+ ('A', ('`', 192) : ('\'', 193) : ('^', 194) : ('~', 195) : ('"', 196) : []) :+ ('E', ('`', 200) : ('\'', 201) : ('^', 202) : ('"', 203) : []) :+ ('I', ('`', 204) : ('\'', 205) : ('^', 206) : ('"', 207) : []) :+ ('N', ('~', 209) : []) :+ ('O', ('`', 210) : ('\'', 211) : ('^', 212) : ('~', 213) : ('"', 214) : []) :+ ('U', ('`', 217) : ('\'', 218) : ('^', 219) : ('"', 220) : []) :+ ('Y', ('"', 223) : ('\'', 221) : []) :+ ('C', ('c', 199) : ('\'', 262) : ('^', 264) : ('.', 266) : ('v', 268) : []) :+ ('S', ('c', 350) : ('\'', 346) : ('^', 348) : ('v', 352) : []) :+ ('a', ('`', 224) : ('\'', 225) : ('^', 227) : ('"', 228) : []) :+ ('e', ('`', 232) : ('\'', 233) : ('^', 234) : ('"', 235) : []) :+ ('i', ('`', 236) : ('\'', 237) : ('^', 238) : ('"', 239) : []) :+ ('n', ('~', 241) : []) :+ ('o', ('`', 242) : ('\'', 243) : ('^', 244) : ('~', 245) : ('"', 246) : []) :+ ('u', ('`', 249) : ('\'', 250) : ('^', 251) : ('"', 252) : []) :+ ('y', ('"', 255) : ('\'', 253) : []) :+ ('c', ('c', 231) : ('\'', 263) : ('^', 265) : ('.', 267) : ('v', 269) : []) :+ ('s', ('c', 351) : ('\'', 347) : ('^', 349) : ('"', 223) : ('v', 353) : []) :+ []++specialChars :: [(String, Int)]+specialChars =+ ("cc", 231) :+ ("cC", 199) :+ ("aa", 229) :+ ("AA", 197) :+ ("i", 239) :+ ("l", 321) :+ ("L", 322) :+ ("ss", 223) :+ ("3", 223) :+ ("o", 248) :+ ("O", 216) :+ ("ae", 230) :+ ("AE", 198) :+ ("S", 167) :+ ("pounds", 163) :+ ("euro", 8364) :+ ("copyright", 169) :++ ("textbackslash", ord '\\') :+ ("textasciitilde", ord '~') :+ ("textasciicircum", ord '^') :+ ("textless", ord '<') :+ ("textgreater", ord '>') :+ ("textdollar", ord '$') :+ ("textexclamdown", 161) :+ ("textquestiondown", 191) :+ ("textquotedblleft", ord '"') :+ []++mathMap :: Map.Map String Char+mathMap =+ Map.fromList (map (\(name,code) -> (name, chr code)) mathChars)++mathChars :: [(String, Int)]+mathChars =+ ("Alpha", 913) :+ ("Beta", 914) :+ ("Gamma", 915) :+ ("Delta", 916) :+ ("Epsilon", 917) :+ ("Zeta", 918) :+ ("Eta", 919) :+ ("Theta", 920) :+ ("Iota", 921) :+ ("Kappa", 922) :+ ("Lambda", 923) :+ ("Mu", 924) :+ ("Nu", 925) :+ ("Xi", 926) :+ ("Omikron", 927) :+ ("Pi", 928) :+ ("Rho", 929) :+ ("Sigma", 931) :+ ("Tau", 932) :+ ("Upsilon", 933) :+ ("Phi", 934) :+ ("Chi", 935) :+ ("Psi", 936) :+ ("Omega", 937) :+ ("alpha", 945) :+ ("beta", 946) :+ ("gamma", 947) :+ ("delta", 948) :+ ("epsilon", 949) :+ ("zeta", 950) :+ ("eta", 951) :+ ("theta", 952) :+ ("iota", 953) :+ ("kappa", 954) :+ ("lambda", 955) :+ ("mu", 956) :+ ("nu", 957) :+ ("xi", 958) :+ ("omikron", 959) :+ ("pi", 960) :+ ("rho", 961) :+ ("sigma", 963) :+ ("tau", 964) :+ ("upsilon", 965) :+ ("phi", 966) :+ ("chi", 967) :+ ("psi", 968) :+ ("omega", 969) :+ []++escapedChars :: [Char]+escapedChars = "$%&_#{}"