packages feed

neil-0.0: src/Paper/Ref.hs

module Paper.Ref(ref) where

import Control.Monad
import Data.Char
import Data.List
import qualified Data.Map as Map
import Paper.Util.Error

data Ref = Ref FilePath Int Bool -- True = ref, False = label
type Refs = Map.Map String [Ref]

addRef m (s,ref) = Map.insertWith (++) s [ref] m


ref :: [FilePath] -> IO ()
ref files = do
    r <- mapM readRefs files
    let errs = checkRefs $ foldl addRef Map.empty $ concat r
    sequence errs
    when (not $ null errs) $
        error $ "Error: " ++ show (length errs) ++ " references failed"
    putStrLn "All references are correct"


readRefs :: FilePath -> IO [(String,Ref)]
readRefs file = liftM (f 1) $ readFile file
    where
        f n ('\n':xs) = f (n+1) xs
        f n ('\\':'r':'e':'f':xs) = g n True xs
        f n ('\\':'l':'a':'b':'e':'l':xs) = g n False xs
        f n (x:xs) = f n xs
        f n [] = []

        g n b ('{':xs) | "}" `isPrefixOf` post = (pre, Ref file n b) : f n (tail post) 
            where (pre,post) = span (`notElem` "\n}") xs
        g n b (x:xs) | isSpace x || x == '{' =
            error $ file ++ "(" ++ show n ++ ") Unrecognised label: " ++ take 25 xs
        g n b xs = f n xs


checkRefs :: Refs -> [IO ()]
checkRefs = concatMap f . Map.toList
    where
        f (s,xs) | null b = err (head a) "Reference used but not defined"
                 | length b > 1 = err (head b) "Reference defined multiple times"
                 | otherwise = []
            where
                (a,b) = partition (\(Ref a b c) -> c) xs
                err (Ref file line _) msg = [errorMsg file line msg ("{" ++ s ++ "}")]