packages feed

haste-compiler-0.4: src/Haste/Args.hs

module Haste.Args (parseArgs) where
import System.Console.GetOpt
import Data.List

-- | Parse a list of command line arguments into a config and a list of args
--   for GHC. Non-options are passed directly to GHC.
parseArgs :: [OptDescr (a -> a)]
          -> String
          -> [String]
          -> Either String (a -> a, [String])
parseArgs opts hdr args
  | "--help" `elem` args || "-?" `elem` args =
    Left $ printHelp hdr opts
  | otherwise =
    let (hasteArgs, ghcArgs) = splitOpts opts args
        (cfgs, _, _) = getOpt Permute opts hasteArgs
    in Right (foldl' (flip (.)) id cfgs, ghcArgs)

-- | Split opts into Haste options and others.
splitOpts :: [OptDescr a] -> [String] -> ([String], [String])
splitOpts opts args =
    (hasteArgs ++ outopts, filter (\x -> take 2 x /= "--") others)
  where
    (hasteArgs, others) =
      partition isHasteOpt nonouts

    (outopts, nonouts) =
      findOutputs ([], [], []) args

    -- TODO: this is a horrible hack to generally mangle arguments until
    --       stuff works - replace ASAP with less hacky solution!
    findOutputs (outs, nouts, fin) ("--libinstall" : xs) =
      findOutputs (outs, nouts, "--libinstall" : fin) xs
    findOutputs (outs, nouts, fin) ("-o" : out : xs) =
      findOutputs (("--out=" ++ out) : outs, nouts, fin) xs
    findOutputs (outs, nouts, fin) ("-outputdir" : out : xs) =
      findOutputs (("--outdir=" ++ out) : outs, nouts, fin) xs
    findOutputs (outs, nouts, fin) (x@('-':'o':out) : xs)
      | out == "hi"        = findOutputs (outs, x:nouts, fin) xs
      | out == "suf"       = findOutputs (outs, x:nouts, fin) xs
      | out == "dir"       = findOutputs (outs, x:nouts, fin) xs
      | take 2 out == "pt" = findOutputs (outs, x:nouts, fin) xs
      | otherwise          = findOutputs (("--out="++out):outs,nouts,fin) xs
    findOutputs (outs, nouts, fin) (x:xs) =
      findOutputs (outs, x : nouts, fin) xs
    findOutputs (outs, nouts, fin) _ =
      (reverse outs ++ reverse fin, reverse nouts)

    isHasteOpt opt
      | "-o" `isPrefixOf` opt = False
      | otherwise             = prefixElem opt optnames

    optnames =
      concatMap names opts

    names (Option short long _ _) =
      map (\c -> ['-',c]) short ++ map ("--" ++) long

-- | Does the given list exist as a prefix of some element in the list of
--   lists?
prefixElem :: Eq a => [a] -> [[a]] -> Bool
prefixElem x = or . map (\y -> take (length y) x == y)

printHelp :: String -> [OptDescr a] -> String
printHelp hdr = (hdr ++) . ("\n" ++) . unlines . map helpString

helpString :: OptDescr a -> String
helpString (Option short long opt help) =
    shorts ++ longs ++ "\n" ++ formatHelpMessage 80 help
  where
    (longarg, shortarg) =
      case opt of
        NoArg _    -> ("", "")
        ReqArg _ a -> ('=':a, ' ':a)
        OptArg _ a -> ("[=" ++ a ++ "]", " [" ++ a ++ "]")
    shorts =
      case intercalate ", " (map (\c -> ['-',c]) short) of
        s | null s    -> ""
          | otherwise -> s ++ shortarg ++ ", "
    longs =
      case intercalate ", " (map (\s -> "--" ++ s) long) of
        l | null l    -> ""
          | otherwise -> l ++ longarg

-- | Break lines at n chars, add two spaces before each.
formatHelpMessage :: Int -> String -> String
formatHelpMessage chars help =
    unlines . map ("  " ++) . breakLines 0 [] $ words help
  where
    breakLines len ln (w:ws)
      | length w >= chars-2     = w:unwords (reverse ln):breakLines 0 [] ws
      | len+length w >= chars-2 = unwords (reverse ln):breakLines 0 [] (w:ws)
      | otherwise               = breakLines (len+1+length w) (w:ln) ws
    breakLines _ ln _ =
      [unwords $ reverse ln]