hnix/Nix/Parser.hs

177 lines
5.5 KiB
Haskell

{-# LANGUAGE CPP #-}
module Nix.Parser (parseNixFile, Result(..)) where
import Control.Applicative
import Control.Monad hiding (forM_, mapM, sequence)
import Control.Monad.IO.Class
import Data.Char
import Data.Foldable
import qualified Data.Map as Map
import Data.Text hiding (concat, concatMap, head, map)
import Nix.Types
import Nix.Internal
import qualified Prelude
import Prelude hiding (readFile, concat, concatMap, elem, mapM,
sequence)
#if USE_PARSEC
import Data.Text.IO
import Text.Parsec hiding ((<|>), many, optional)
import Text.Parsec.Text
import Text.PrettyPrint.ANSI.Leijen (Doc, text)
symbol :: String -> Parser String
symbol str = string str <* whiteSpace
symbolic :: Char -> Parser Char
symbolic c = char c <* whiteSpace
decimal :: Parser Integer
decimal = read <$> some digit
whiteSpace :: Parser ()
whiteSpace = spaces
data Result a = Success a
| Failure Doc
parseFromFileEx :: MonadIO m => Parser a -> FilePath -> m (Result a)
parseFromFileEx p path =
(either (Failure . text . show) Success . parse p path)
`liftM` liftIO (readFile path)
#else
import Text.Trifecta
import Text.Parser.LookAhead
#endif
parseNixFile :: MonadIO m => FilePath -> m (Result NExpr)
parseNixFile = parseFromFileEx nixApp
nixApp :: Parser NExpr
nixApp = go <$> some (whiteSpace *> nixTerm True)
where
go [] = error "some has failed us"
go [x] = x
go (f:xs) = Fix (NApp f (go xs))
nixTerm :: Bool -> Parser NExpr
nixTerm allowLambdas = choice
[ mkInt <$> decimal <?> "integer"
, (string "true" *> pure (mkBool True)) <?> "bool"
, (string "false" *> pure (mkBool False)) <?> "bool"
, (string "null" *> pure mkNull) <?> "null"
, between (symbolic '(') (symbolic ')') nixApp
<?> "parens"
, between (symbolic '[') (symbolic ']')
(Fix . NList <$> many (nixTerm False))
<?> "list"
, try (do chars <- some (satisfy isPathChar)
trace ("Path chars: " ++ show chars) $ return ()
guard ('/' `elem` chars)
return $ mkPath chars)
, maybeSetOrLambda allowLambdas
]
maybeSetOrLambda :: Bool -> Parser NExpr
maybeSetOrLambda allowLambdas = do
trace "maybeSetOrLambda" $ return ()
x <- try (lookAhead symName)
<|> try (lookAhead (singleton <$> char '{'))
<|> return ""
if x == "rec" || x == "{"
then setOrArgs
else do
trace "might still have a lambda" $ return ()
y <- try (lookAhead (symName *> whiteSpace *> symbolic ':'
*> return True))
<|> return False
trace ("results are = " ++ show y) $ return ()
if y
then if allowLambdas
then setOrArgs
else error "Unexpected lambda"
else keyName <?> "string"
isPathChar :: Char -> Bool
isPathChar c = isAlpha c || c `Prelude.elem` ".:/"
oneChar :: Parser NExpr
oneChar = mkStr . singleton <$> anyChar
stringChar :: Parser NExpr
stringChar = char '\\' *> oneChar
<|> (string "${" *> nixApp <* char '}')
<|> (mkStr . pack <$> many (noneOf "\"\\"))
symName :: Parser Text
symName = do
chars <- some (satisfy (\c -> isAlpha c || c == '.'))
trace ("chars = " ++ show chars) $ return ()
guard (isLower (head chars))
return $ pack (trace ("chars: " ++ show chars) chars)
stringish :: Parser NExpr
stringish
= (char '"' *>
(Fix . NConcat <$> manyTill stringChar (char '"')))
<|> (char '$' *> between (symbolic '{') (symbolic '}') nixApp)
keyName :: Parser NExpr
keyName = (stringish <|> (mkSym <$> symName)) <* whiteSpace
nvPair :: Parser (NExpr, NExpr)
nvPair = (,) <$> keyName
<*> (symbolic '=' *> nixApp)
argExpr :: Parser NExpr
argExpr = (Fix . NArgSet . Map.fromList <$> argList)
<|> ((mkSym <$> symName) <?> "argname")
argList :: Parser [(Text, Maybe NExpr)]
argList = between (symbolic '{') (symbolic '}')
((argName <* whiteSpace) `sepBy` symbolic ',')
<?> "arglist"
argName :: Parser (Text, Maybe NExpr)
argName = (,) <$> (symName <* whiteSpace)
<*> optional (try (symbolic '?' *> nixApp))
-- whiteSymbolic :: Char -> Parser Char
-- whiteSymbolic c = whiteSpace *> symbolic c
lookaheadForSet :: Parser Bool
lookaheadForSet = do
trace "lookaheadForSet" $ return ()
x <- (symbolic '{' *> return True) <|> return False
if not x then return x else do
y <- (keyName *> return True) <|> return False
if not y then return y else
(symbolic '=' *> return True) <|> return False
setOrArgs :: Parser NExpr
setOrArgs = do
trace "setOrArgs" $ return ()
sawRec <- try (symbol "rec" *> pure True) <|> pure False
trace ("Do we have sawRec: " ++ show sawRec) $ return ()
haveSet <-
if sawRec
then return True
else try (lookAhead lookaheadForSet)
trace ("Do we have a set: " ++ show haveSet) $ return ()
if haveSet
then between (symbolic '{') (symbolic '}')
(Fix . NSet sawRec <$> nvPair `endBy` symbolic ';')
<?> "set"
else do
trace "parsing arguments" $ return ()
args <- argExpr <?> "arguments"
trace ("args: " ++ show args) $ return ()
symbolic ':' *> ((Fix .) . NAbs <$> pure args <*> nixApp)
<|> pure args