Switch to a Parsec expression table

This commit is contained in:
John Wiegley 2014-07-01 03:11:26 -05:00
parent be1434726a
commit 682f039023
4 changed files with 79 additions and 50 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
/Setup /Setup
/dist/

View file

@ -17,17 +17,16 @@ import qualified Prelude
import Prelude hiding (elem) import Prelude hiding (elem)
nixApp :: Parser NExpr nixApp :: Parser NExpr
nixApp = go <$> some (whiteSpace *> nixExpr True) nixApp = go <$> someTill (whiteSpace *> nixExpr True)
(try (lookAhead (char ';')))
where where
go [] = error "some has failed us" go [] = error "some has failed us"
go [x] = x go [x] = x
go (f:xs) = Fix (NApp f (go xs)) go (f:xs) = Fix (NApp f (go xs))
nixExpr :: Bool -> Parser NExpr nixExpr :: Bool -> Parser NExpr
nixExpr allowLambdas = nixExpr = buildExpressionParser table . nixTerm
buildExpressionParser table (nixTerm allowLambdas) <?> "expression" where
where
table :: OperatorTable Parser NExpr
table = table =
[ [ prefix "-" NNeg ] [ [ prefix "-" NNeg ]
, [ binary "++" NConcat AssocRight ] , [ binary "++" NConcat AssocRight ]
@ -45,7 +44,7 @@ nixExpr allowLambdas =
-- Postfix (pure (Fix . NOper . fun) <* symbol name) -- Postfix (pure (Fix . NOper . fun) <* symbol name)
nixTerm :: Bool -> Parser NExpr nixTerm :: Bool -> Parser NExpr
nixTerm allowLambdas = choice nixTerm allowLambdas = trace "in nixTerm" (return ()) >> choice
[ nixInt [ nixInt
, nixBool , nixBool
, nixNull , nixNull
@ -67,12 +66,12 @@ nixNull :: Parser NExpr
nixNull = string "null" *> pure mkNull <?> "null" nixNull = string "null" *> pure mkNull <?> "null"
nixParens :: Parser NExpr nixParens :: Parser NExpr
nixParens = between (symbolic '(') (symbolic ')') nixApp <?> "parens" nixParens = parens nixApp <?> "parens"
nixList :: Parser NExpr nixList :: Parser NExpr
nixList = between (symbolic '[') (symbolic ']') nixList = do
(Fix . NList <$> many (nixTerm False)) trace "in nixList" $ return ()
<?> "list" brackets (Fix . NList <$> many (nixTerm False)) <?> "list"
nixPath :: Parser NExpr nixPath :: Parser NExpr
nixPath = try $ do nixPath = try $ do
@ -112,9 +111,8 @@ symName = do
stringish :: Parser NExpr stringish :: Parser NExpr
stringish stringish
= (char '"' *> = (char '"' *> (merge <$> manyTill stringChar (char '"')))
(merge <$> manyTill stringChar (char '"'))) <|> (char '$' *> braces nixApp)
<|> (char '$' *> between (symbolic '{') (symbolic '}') nixApp)
where where
merge = foldl1' (\x y -> Fix (NOper (NConcat x y))) merge = foldl1' (\x y -> Fix (NOper (NConcat x y)))
@ -126,18 +124,25 @@ stringish
oneChar = mkStr . singleton <$> anyChar oneChar = mkStr . singleton <$> anyChar
argExpr :: Parser NExpr argExpr :: Parser NExpr
argExpr = (Fix . NArgSet . Map.fromList <$> argList) argExpr = do
<|> ((mkSym <$> symName) <?> "argname") trace "in argExpr" $ return ()
(Fix . NArgSet . Map.fromList <$> argList)
<|> ((mkSym <$> symName) <?> "argname")
where where
argList = between (symbolic '{') (symbolic '}') argList = do
((argName <* whiteSpace) `sepBy` symbolic ',') trace "in argList" $ return ()
<?> "arglist" braces ((argName <* trace "FOO" whiteSpace) `sepBy` trace "BAR" (symbolic ','))
<?> "arglist"
argName = (,) <$> (symName <* whiteSpace) argName = do
<*> optional (symbolic '?' *> nixTerm False) trace "in argName" $ return ()
(,) <$> (symName <* whiteSpace)
<*> optional (symbolic '?' *> nixExpr False)
nvPair :: Parser (NExpr, NExpr) nvPair :: Parser (NExpr, NExpr)
nvPair = (,) <$> keyName <*> (symbolic '=' *> nixApp) nvPair = do
trace "in nvPair" $ return ()
(,) <$> keyName <*> (symbolic '=' *> nixApp)
keyName :: Parser NExpr keyName :: Parser NExpr
keyName = (stringish <|> (mkSym <$> symName)) <* whiteSpace keyName = (stringish <|> (mkSym <$> symName)) <* whiteSpace
@ -153,9 +158,8 @@ setOrArgs = do
else try (lookAhead lookaheadForSet) else try (lookAhead lookaheadForSet)
trace ("Do we have a set: " ++ show haveSet) $ return () trace ("Do we have a set: " ++ show haveSet) $ return ()
if haveSet if haveSet
then between (symbolic '{') (symbolic '}') then braces (Fix . NSet sawRec <$> nvPair `endBy` symbolic ';')
(Fix . NSet sawRec <$> nvPair `endBy` symbolic ';') <?> "set"
<?> "set"
else do else do
trace "parsing arguments" $ return () trace "parsing arguments" $ return ()
args <- argExpr <?> "arguments" args <- argExpr <?> "arguments"
@ -169,7 +173,8 @@ lookaheadForSet = do
x <- (symbolic '{' *> return True) <|> return False x <- (symbolic '{' *> return True) <|> return False
if not x then return x else do if not x then return x else do
y <- (keyName *> return True) <|> return False y <- (keyName *> return True) <|> return False
if not y then return y else if not y then return y else do
trace "still in lookaheadForSet" $ return ()
(symbolic '=' *> return True) <|> return False (symbolic '=' *> return True) <|> return False
parseNixFile :: MonadIO m => FilePath -> m (Result NExpr) parseNixFile :: MonadIO m => FilePath -> m (Result NExpr)

View file

@ -1,27 +1,43 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
module Nix.Parser.Library module Nix.Parser.Library ( module Nix.Parser.Library, module X ) where
(
#if USE_PARSEC import Control.Applicative
module Text.Parsec
, module Text.Parsec.Expr
, module Text.Parsec.Text
#else
module Text.Trifecta
, module Text.Parser.Expression
, module Text.Parser.LookAhead
#endif
)
where
#if USE_PARSEC #if USE_PARSEC
import Control.Applicative import Control.Monad
import Data.Text.IO import Control.Monad.IO.Class
import Text.Parsec hiding ((<|>), many, optional) import Data.Text.IO as T
import Text.Parsec.Expr import Text.Parsec as X hiding ((<|>), many, optional)
import Text.Parsec.Text import Text.Parsec.Expr as X
import Text.PrettyPrint.ANSI.Leijen (Doc, text) import Text.Parsec.Text as X
import qualified Text.Parsec.Token as P
import Text.PrettyPrint.ANSI.Leijen as X (Doc, text)
lexer :: Stream s m Char => P.GenTokenParser s u m
lexer = P.makeTokenParser P.LanguageDef
{ P.commentStart = "/*"
, P.commentEnd = "*/"
, P.commentLine = "#"
, P.nestedComments = True
, P.identStart = letter <|> char '_'
, P.identLetter = alphaNum <|> char '_'
, P.opStart = oneOf ":!#$%&*+./<=>?@\\^|-~"
, P.opLetter = oneOf "@"
, P.reservedNames = []
, P.reservedOpNames = []
, P.caseSensitive = True
}
parens :: Parser a -> Parser a
parens = P.parens lexer
brackets :: Parser a -> Parser a
brackets = P.brackets lexer
braces :: Parser a -> Parser a
braces = P.braces lexer
symbol :: String -> Parser String symbol :: String -> Parser String
symbol str = string str <* whiteSpace symbol str = string str <* whiteSpace
@ -33,7 +49,7 @@ decimal :: Parser Integer
decimal = read <$> some digit decimal = read <$> some digit
whiteSpace :: Parser () whiteSpace :: Parser ()
whiteSpace = spaces whiteSpace = P.whiteSpace lexer
data Result a = Success a data Result a = Success a
| Failure Doc | Failure Doc
@ -41,12 +57,19 @@ data Result a = Success a
parseFromFileEx :: MonadIO m => Parser a -> FilePath -> m (Result a) parseFromFileEx :: MonadIO m => Parser a -> FilePath -> m (Result a)
parseFromFileEx p path = parseFromFileEx p path =
(either (Failure . text . show) Success . parse p path) (either (Failure . text . show) Success . parse p path)
`liftM` liftIO (readFile path) `liftM` liftIO (T.readFile path)
#else #else
import Text.Parser.Expression import Text.Parser.Expression as X
import Text.Parser.LookAhead import Text.Parser.LookAhead as X
import Text.Trifecta import Text.Parser.Token as X
import Text.Trifecta as X
#endif #endif
someTill :: Parser a -> Parser end -> Parser [a]
someTill p end = go *> scan
where
go = do { x <- p; xs <- scan; return (x:xs) }
scan = (end *> return []) <|> go

View file

@ -1,5 +1,5 @@
{ cabal, parsers, trifecta, text, ansiWlPprint, parsec, transformers { cabal, parsers, trifecta, text, ansiWlPprint, parsec, transformers
, useParsec ? false , useParsec ? true
}: }:
cabal.mkDerivation (self: rec { cabal.mkDerivation (self: rec {