177 lines
5.5 KiB
Haskell
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
|