Plan 9 from Bell Labs’s /usr/web/sources/contrib/fernan/nhc98/src/libraries/CabalParse.hs

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


module CabalParse where

import IO
import Char
import System
import Text.ParserCombinators.Poly

-- | "cabalLookup" parses any particular request field from an already-parsed
--   cabal file.
--   The first arg is the contents of the .cabal file, the second is the
--   field name to extract.  The third arg manipulates the result string
--   e.g. to translate module names from Haskell notation to filepaths.
cabalLookup :: CabalFile -> String -> (String->String) -> Either String String
cabalLookup cabal field munge = do
    case lookup field cabal of
      Just rhs -> case runParser (fieldtype field) rhs of
                    (Left e, _) -> Left e
                    (Right result, _) -> Right (munge result)
      Nothing  -> Left ("field "++field++" not present")

stop :: String -> IO a
stop s = do hPutStrLn stderr ("cabal-parse:\n"++indent 2 s)
            exitFailure
            return undefined
quiet :: String -> IO ()
quiet s = return ()

slash = map (\c -> if c=='.' then '/' else c)

-- Mapping from field-name to type of rhs
fieldtype :: String -> Parser Token String
fieldtype f = case f of
                "exposed-modules" -> unLines (list string)
                "other-modules"   -> unLines (list string)
                "c-sources"       -> unLines (list string)
                "hs-source-dirs"  -> unLines (list string)
                "hs-source-dir"   -> string
                "build-depends"   -> unLines (commalist string)
                "extensions"      -> unLines (list string)
                "data-files"      -> unLines (list string)
                "extra-source-files" -> unLines (list string)
                "extra-tmp-files" -> unLines (list string)
                "homepage"        -> url
                "package-url"     -> url
                _                 -> freetext

-- Simple Lexer:
data Token = Word String | Colon | Comma
           | Newline | NewlineIndent
           deriving (Eq,Show)

lexToken :: String -> [Token]
lexToken    []        = []
lexToken (':':ss)     = Colon : lexToken ss
lexToken ('\n':ss)    = case ss of
                          ('-':'-':_)        -> NewlineIndent: lexToken ss
                          ('\n':cs)          -> Newline      : lexToken cs
                          (c:cs) | isSpace c -> NewlineIndent: lexToken cs
                          _                  -> Newline : lexToken ss
lexToken ('-':'-':ss) = dropWhile (`notElem`[Newline,NewlineIndent])
                                  (lexToken ss)
lexToken (',':c:ss)   | isSpace c = Comma : lexToken (c:ss)
lexToken (c:ss)       | isSpace c = lexToken ss
lexToken (c:ss)       = accumulate [c] ss

accumulate :: String -> String -> [Token]
accumulate acc []     =  []
accumulate acc (c:cs) | isSpace c || c `elem` ":,"
                      = Word (reverse acc) : lexToken (c:cs)
accumulate acc (c:cs) = accumulate (c:acc) cs

-- Simple Parsers:
type CabalFile = [(String,[Token])]

-- parse a bunch of keyword/value bindings, without further
-- interpretation of the rhs
cabalFile :: Parser Token CabalFile
cabalFile = many1 $ do
    (Word key) <- next
    Colon      <- next `adjustErr` (("Missing colon after "++key++"\n")++)
    rhs        <- manyFinally (satisfy (/=Newline)) (has Newline)
    return (map toLower key, rhs)

-- parse a single definite token
has :: Token -> Parser Token ()
has t = do satisfy (==t); return ()

-- parse something whilst ignoring non-significant newlines
acrossLines p = do many (has NewlineIndent)
                   x <- p
                   many (has NewlineIndent)
                   return x

-- parse freeform text
freetext :: Parser Token String
freetext = free ""
  where free s = do n <- next
                    case n of
                      Word w        -> free (reverse w++" "++s)
                      Colon         -> free (':':s)
                      Comma         -> free (',':s)
                      NewlineIndent -> free ('\n':s)
                 `onFail` return (reverse s)

-- parse URL, like freeform text only all spaces squashed
url :: Parser Token String
url = freetext >>= return . filter (not.isSpace)

-- parse a comma-separated list of items
commalist :: Parser Token a -> Parser Token [a]
commalist item = acrossLines (item `sepBy` acrossLines (has Comma))

-- parse a (possibly) comma-separated list of items
list :: Parser Token a -> Parser Token [a]
list item = acrossLines (item `sepBy` acrossLines (optional (has Comma)))

-- parse a single module name, directory name, package name, etc
string :: Parser Token String
string = do (Word w) <- next; return w

-- convert a list of strings into a whitespace-separated string
unLines :: Parser Token [String] -> Parser Token String
unLines p = p >>= return . init . unlines

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.