Merge pull request #35 from expipiplus1/annotate
WIP: Annotate expressions with their location
This commit is contained in:
commit
e5f64d5387
|
@ -1,8 +1,10 @@
|
|||
-- | Wraps the expression submodules.
|
||||
module Nix.Expr (
|
||||
module Nix.Expr.Types,
|
||||
module Nix.Expr.Types.Annotated,
|
||||
module Nix.Expr.Shorthands
|
||||
) where
|
||||
|
||||
import Nix.Expr.Types
|
||||
import Nix.Expr.Shorthands
|
||||
import Nix.Expr.Types.Annotated
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
-- | A bunch of shorthands for making nix expressions.
|
||||
--
|
||||
-- Functions with an @F@ suffix return a more general type without the outer
|
||||
-- 'Fix' wrapper.
|
||||
module Nix.Expr.Shorthands where
|
||||
|
||||
import Prelude
|
||||
|
@ -11,7 +14,10 @@ import Nix.Expr.Types
|
|||
|
||||
-- | Make an integer literal expression.
|
||||
mkInt :: Integer -> NExpr
|
||||
mkInt = Fix . NConstant . NInt
|
||||
mkInt = Fix . mkIntF
|
||||
|
||||
mkIntF :: Integer -> NExprF a
|
||||
mkIntF = NConstant . NInt
|
||||
|
||||
-- | Make a regular (double-quoted) string.
|
||||
mkStr :: Text -> NExpr
|
||||
|
@ -27,34 +33,55 @@ mkIndentedStr = Fix . NStr . Indented . \case
|
|||
|
||||
-- | Make a literal URI expression.
|
||||
mkUri :: Text -> NExpr
|
||||
mkUri = Fix . NConstant . NUri
|
||||
mkUri = Fix . mkUriF
|
||||
|
||||
mkUriF :: Text -> NExprF a
|
||||
mkUriF = NConstant . NUri
|
||||
|
||||
-- | Make a path. Use 'True' if the path should be read from the
|
||||
-- environment, else 'False'.
|
||||
mkPath :: Bool -> FilePath -> NExpr
|
||||
mkPath False = Fix . NLiteralPath
|
||||
mkPath True = Fix . NEnvPath
|
||||
mkPath b = Fix . mkPathF b
|
||||
|
||||
mkPathF :: Bool -> FilePath -> NExprF a
|
||||
mkPathF False = NLiteralPath
|
||||
mkPathF True = NEnvPath
|
||||
|
||||
-- | Make a path expression which pulls from the NIX_PATH env variable.
|
||||
mkEnvPath :: FilePath -> NExpr
|
||||
mkEnvPath = mkPath True
|
||||
mkEnvPath = Fix . mkEnvPathF
|
||||
|
||||
mkEnvPathF :: FilePath -> NExprF a
|
||||
mkEnvPathF = mkPathF True
|
||||
|
||||
-- | Make a path expression which references a relative path.
|
||||
mkRelPath :: FilePath -> NExpr
|
||||
mkRelPath = mkPath False
|
||||
mkRelPath = Fix . mkRelPathF
|
||||
|
||||
mkRelPathF :: FilePath -> NExprF a
|
||||
mkRelPathF = mkPathF False
|
||||
|
||||
-- | Make a variable (symbol)
|
||||
mkSym :: Text -> NExpr
|
||||
mkSym = Fix . NSym
|
||||
mkSym = Fix . mkSymF
|
||||
|
||||
mkSymF :: Text -> NExprF a
|
||||
mkSymF = NSym
|
||||
|
||||
mkSelector :: Text -> NAttrPath NExpr
|
||||
mkSelector = (:[]) . StaticKey
|
||||
|
||||
mkBool :: Bool -> NExpr
|
||||
mkBool = Fix . NConstant . NBool
|
||||
mkBool = Fix . mkBoolF
|
||||
|
||||
mkBoolF :: Bool -> NExprF a
|
||||
mkBoolF = NConstant . NBool
|
||||
|
||||
mkNull :: NExpr
|
||||
mkNull = Fix (NConstant NNull)
|
||||
mkNull = Fix mkNullF
|
||||
|
||||
mkNullF :: NExprF a
|
||||
mkNullF = NConstant NNull
|
||||
|
||||
mkOper :: NUnaryOp -> NExpr -> NExpr
|
||||
mkOper op = Fix . NUnary op
|
||||
|
|
|
@ -13,6 +13,7 @@ import Control.Monad hiding (forM_, mapM, sequence)
|
|||
import Data.Data
|
||||
import Data.Fix
|
||||
import Data.Foldable
|
||||
import Data.Functor.Classes (Show1(..))
|
||||
import Data.Map (Map)
|
||||
import Data.Text (Text, pack)
|
||||
import Data.Traversable
|
||||
|
@ -71,6 +72,9 @@ data NExprF r
|
|||
-- ^ Assert that the first returns true before evaluating the second.
|
||||
deriving (Ord, Eq, Generic, Typeable, Data, Functor, Show)
|
||||
|
||||
instance Show1 NExprF where
|
||||
showsPrec1 = showsPrec
|
||||
|
||||
-- | We make an `IsString` for expressions, where the string is interpreted
|
||||
-- as an identifier. This is the most common use-case...
|
||||
instance IsString NExpr where
|
||||
|
|
87
Nix/Expr/Types/Annotated.hs
Normal file
87
Nix/Expr/Types/Annotated.hs
Normal file
|
@ -0,0 +1,87 @@
|
|||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
{-# LANGUAGE DeriveFunctor #-}
|
||||
{-# LANGUAGE PatternSynonyms #-}
|
||||
{-# LANGUAGE DeriveGeneric #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
-- | The source location annotated nix expression type and supporting types.
|
||||
--
|
||||
module Nix.Expr.Types.Annotated
|
||||
( module Nix.Expr.Types.Annotated
|
||||
, Delta(..)
|
||||
)where
|
||||
|
||||
import Control.Monad hiding (forM_, mapM, sequence)
|
||||
import Data.Data
|
||||
import Data.Fix
|
||||
import Data.Function (on)
|
||||
import Data.Functor.Classes (Show1(..))
|
||||
import Data.Functor.Compose
|
||||
import Data.Semigroup
|
||||
import GHC.Generics
|
||||
import Nix.Expr.Types
|
||||
import Nix.Parser.Library (Delta(..))
|
||||
import Prelude hiding (concat, concatMap, elem, foldr,
|
||||
mapM, minimum, readFile, sequence)
|
||||
|
||||
-- | A location in a source file
|
||||
data SrcSpan = SrcSpan{ spanBegin :: Delta
|
||||
, spanEnd :: Delta
|
||||
}
|
||||
deriving (Ord, Eq, Generic, Typeable, Data, Show)
|
||||
|
||||
-- | A type constructor applied to a type along with an annotation
|
||||
--
|
||||
-- Intended to be used with 'Fix':
|
||||
-- @type MyType = Fix (Compose (Ann Annotation) F)@
|
||||
data Ann ann a = Ann{ annotation :: ann
|
||||
, annotated :: a
|
||||
}
|
||||
deriving (Ord, Eq, Data, Generic, Typeable, Functor, Read, Show)
|
||||
|
||||
instance Show ann => Show1 (Ann ann) where
|
||||
showsPrec1 = showsPrec
|
||||
|
||||
instance Semigroup SrcSpan where
|
||||
s1 <> s2 = SrcSpan ((min `on` spanBegin) s1 s2)
|
||||
((max `on` spanEnd) s1 s2)
|
||||
|
||||
type AnnF ann f = Compose (Ann ann) f
|
||||
|
||||
annToAnnF :: Ann ann (f (Fix (AnnF ann f))) -> Fix (AnnF ann f)
|
||||
annToAnnF (Ann ann a) = AnnE ann a
|
||||
|
||||
type NExprLocF = AnnF SrcSpan NExprF
|
||||
|
||||
-- | A nix expression with source location at each subexpression.
|
||||
type NExprLoc = Fix NExprLocF
|
||||
|
||||
pattern AnnE ann a = Fix (Compose (Ann ann a))
|
||||
|
||||
stripAnnotation :: Functor f => Fix (AnnF ann f) -> Fix f
|
||||
stripAnnotation = ana (annotated . getCompose . unFix)
|
||||
|
||||
nApp :: NExprLoc -> NExprLoc -> NExprLoc
|
||||
nApp e1@(AnnE s1 _) e2@(AnnE s2 _) = AnnE (s1 <> s2) (NApp e1 e2)
|
||||
|
||||
nUnary :: Ann SrcSpan NUnaryOp -> NExprLoc -> NExprLoc
|
||||
nUnary (Ann s1 u) e1@(AnnE s2 _) = AnnE (s1 <> s2) (NUnary u e1)
|
||||
|
||||
nBinary :: Ann SrcSpan NBinaryOp -> NExprLoc -> NExprLoc -> NExprLoc
|
||||
nBinary (Ann s1 b) e1@(AnnE s2 _) e2@(AnnE s3 _) =
|
||||
AnnE (s1 <> s2 <> s3) (NBinary b e1 e2)
|
||||
|
||||
nSelectLoc :: NExprLoc -> Ann SrcSpan (NAttrPath NExprLoc) -> Maybe NExprLoc -> NExprLoc
|
||||
nSelectLoc e1@(AnnE s1 _) (Ann s2 ats) d = case d of
|
||||
Nothing -> AnnE (s1 <> s2) (NSelect e1 ats Nothing)
|
||||
Just (e2@(AnnE s3 _)) -> AnnE (s1 <> s2 <> s3) (NSelect e1 ats (Just e2))
|
||||
|
||||
nHasAttr :: NExprLoc -> Ann SrcSpan (NAttrPath NExprLoc) -> NExprLoc
|
||||
nHasAttr e1@(AnnE s1 _) (Ann s2 ats) = AnnE (s1 <> s2) (NHasAttr e1 ats)
|
||||
|
||||
nAbs :: Ann SrcSpan (Params NExprLoc) -> NExprLoc -> NExprLoc
|
||||
nAbs (Ann s1 ps) e1@(AnnE s2 _) = AnnE (s1 <> s2) (NAbs ps e1)
|
||||
|
||||
nStr :: Ann SrcSpan (NString NExprLoc) -> NExprLoc
|
||||
nStr (Ann s1 s) = AnnE s1 (NStr s)
|
178
Nix/Parser.hs
178
Nix/Parser.hs
|
@ -3,15 +3,17 @@
|
|||
|
||||
module Nix.Parser (
|
||||
parseNixFile,
|
||||
parseNixFileLoc,
|
||||
parseNixString,
|
||||
parseNixStringLoc,
|
||||
parseNixText,
|
||||
parseNixTextLoc,
|
||||
Result(..)
|
||||
) where
|
||||
|
||||
import Control.Applicative
|
||||
import Control.Monad
|
||||
import Control.Monad.IO.Class
|
||||
import Data.Fix
|
||||
import Data.Foldable hiding (concat)
|
||||
import qualified Data.Map as Map
|
||||
import Data.Text hiding (head, map, foldl1', foldl', concat)
|
||||
|
@ -21,76 +23,99 @@ import Nix.Expr
|
|||
import Nix.StringOperations
|
||||
import Prelude hiding (elem)
|
||||
|
||||
-- | The lexer for this parser is defined in 'Nix.Parser.Library'.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
annotateLocation :: Parser a -> Parser (Ann SrcSpan a)
|
||||
annotateLocation p = do
|
||||
begin <- position
|
||||
res <- p
|
||||
end <- position
|
||||
let span = SrcSpan begin end
|
||||
pure $ Ann span res
|
||||
|
||||
annotateLocation1 :: Parser (NExprF NExprLoc) -> Parser NExprLoc
|
||||
annotateLocation1 = fmap annToAnnF . annotateLocation
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
nixExpr :: Parser NExpr
|
||||
nixExpr = whiteSpace *> (nixToplevelForm <|> foldl' makeParser nixTerm nixOperators)
|
||||
nixExpr = stripAnnotation <$> nixExprLoc
|
||||
|
||||
-- | The lexer for this parser is defined in 'Nix.Parser.Library'.
|
||||
nixExprLoc :: Parser NExprLoc
|
||||
nixExprLoc = whiteSpace *> (nixToplevelForm <|> foldl' makeParser nixTerm nixOperators)
|
||||
where
|
||||
makeParser :: Parser NExprLoc -> Either NSpecialOp NOperatorDef -> Parser NExprLoc
|
||||
makeParser term (Left NSelectOp) = nixSelect term
|
||||
makeParser term (Left NAppOp) = chainl1 term $ pure $ \a b -> Fix (NApp a b)
|
||||
makeParser term (Left NAppOp) = chainl1 term $ pure $ \a b -> (nApp a b)
|
||||
makeParser term (Left NHasAttrOp) = nixHasAttr term
|
||||
makeParser term (Right (NUnaryDef name op))
|
||||
= build <$> many (void $ symbol name) <*> term
|
||||
where build = flip $ foldl' (\t' () -> mkOper op t')
|
||||
= build <$> many (annotateLocation (void $ symbol name)) <*> term
|
||||
where build :: [Ann SrcSpan ()] -> NExprLoc -> NExprLoc
|
||||
build = flip $ foldl' (\t' (Ann s ()) -> nUnary (Ann s op) t')
|
||||
makeParser term (Right (NBinaryDef assoc ops)) = case assoc of
|
||||
NAssocLeft -> chainl1 term op
|
||||
NAssocRight -> chainr1 term op
|
||||
NAssocNone -> term <**> (flip <$> op <*> term <|> pure id)
|
||||
where op = choice . map (\(n,o) -> mkOper2 o <$ reservedOp n) $ ops
|
||||
where op :: Parser (NExprLoc -> NExprLoc -> NExprLoc)
|
||||
op = choice . map (\(n,o) -> (\(Ann a ()) -> nBinary (Ann a o)) <$> annotateLocation (reservedOp n)) $ ops
|
||||
|
||||
antiStart :: Parser String
|
||||
antiStart = try (string "${") <?> show ("${" :: String)
|
||||
|
||||
nixAntiquoted :: Parser a -> Parser (Antiquoted a NExpr)
|
||||
nixAntiquoted p = Antiquoted <$> (antiStart *> nixExpr <* symbolic '}') <|> Plain <$> p
|
||||
nixAntiquoted :: Parser a -> Parser (Antiquoted a NExprLoc)
|
||||
nixAntiquoted p = Antiquoted <$> (antiStart *> nixExprLoc <* symbolic '}') <|> Plain <$> p
|
||||
|
||||
selDot :: Parser ()
|
||||
selDot = try (char '.' *> notFollowedBy (("path" :: String) <$ nixPath)) *> whiteSpace
|
||||
<?> "."
|
||||
|
||||
nixSelector :: Parser (NAttrPath NExpr)
|
||||
nixSelector = keyName `sepBy1` selDot where
|
||||
nixSelector :: Parser (Ann SrcSpan (NAttrPath NExprLoc))
|
||||
nixSelector = annotateLocation $ keyName `sepBy1` selDot
|
||||
|
||||
nixSelect :: Parser NExpr -> Parser NExpr
|
||||
nixSelect :: Parser NExprLoc -> Parser NExprLoc
|
||||
nixSelect term = build
|
||||
<$> term
|
||||
<*> optional ((,) <$> (selDot *> nixSelector) <*> optional (reserved "or" *> nixExpr))
|
||||
<*> optional ((,) <$> (selDot *> nixSelector) <*> optional (reserved "or" *> nixExprLoc))
|
||||
where
|
||||
build :: NExprLoc -> Maybe (Ann SrcSpan (NAttrPath NExprLoc), Maybe NExprLoc) -> NExprLoc
|
||||
build t Nothing = t
|
||||
build t (Just (s,o)) = Fix $ NSelect t s o
|
||||
build t (Just (s,o)) = nSelectLoc t s o
|
||||
|
||||
nixHasAttr :: Parser NExpr -> Parser NExpr
|
||||
nixHasAttr :: Parser NExprLoc -> Parser NExprLoc
|
||||
nixHasAttr term = build <$> term <*> optional (reservedOp "?" *> nixSelector) where
|
||||
build :: NExprLoc -> Maybe (Ann SrcSpan (NAttrPath NExprLoc)) -> NExprLoc
|
||||
build t Nothing = t
|
||||
build t (Just s) = Fix $ NHasAttr t s
|
||||
build t (Just s) = nHasAttr t s
|
||||
|
||||
-- | A self-contained unit.
|
||||
nixTerm :: Parser NExpr
|
||||
nixTerm :: Parser NExprLoc
|
||||
nixTerm = nixSelect $ choice
|
||||
[ nixInt, nixBool, nixNull, nixParens, nixList, nixPath, nixSPath, nixUri
|
||||
, nixStringExpr, nixSet, nixSym ]
|
||||
|
||||
nixToplevelForm :: Parser NExpr
|
||||
nixToplevelForm :: Parser NExprLoc
|
||||
nixToplevelForm = choice [nixLambda, nixLet, nixIf, nixAssert, nixWith]
|
||||
|
||||
nixSym :: Parser NExpr
|
||||
nixSym = mkSym <$> identifier
|
||||
nixSym :: Parser NExprLoc
|
||||
nixSym = annotateLocation1 $ mkSymF <$> identifier
|
||||
|
||||
nixInt :: Parser NExpr
|
||||
nixInt = mkInt <$> token decimal <?> "integer"
|
||||
nixInt :: Parser NExprLoc
|
||||
nixInt = annotateLocation1 $ mkIntF <$> token decimal <?> "integer"
|
||||
|
||||
nixBool :: Parser NExpr
|
||||
nixBool = try (true <|> false) <?> "bool" where
|
||||
true = mkBool True <$ symbol "true"
|
||||
false = mkBool False <$ symbol "false"
|
||||
nixBool :: Parser NExprLoc
|
||||
nixBool = annotateLocation1 $ try (true <|> false) <?> "bool" where
|
||||
true = mkBoolF True <$ symbol "true"
|
||||
false = mkBoolF False <$ symbol "false"
|
||||
|
||||
nixNull :: Parser NExpr
|
||||
nixNull = mkNull <$ try (symbol "null") <?> "null"
|
||||
nixNull :: Parser NExprLoc
|
||||
nixNull = annotateLocation1 $ mkNullF <$ try (symbol "null") <?> "null"
|
||||
|
||||
nixParens :: Parser NExpr
|
||||
nixParens = parens nixExpr <?> "parens"
|
||||
nixParens :: Parser NExprLoc
|
||||
nixParens = parens nixExprLoc <?> "parens"
|
||||
|
||||
nixList :: Parser NExpr
|
||||
nixList = brackets (Fix . NList <$> many nixTerm) <?> "list"
|
||||
nixList :: Parser NExprLoc
|
||||
nixList = annotateLocation1 $ brackets (NList <$> many nixTerm) <?> "list"
|
||||
|
||||
pathChars :: String
|
||||
pathChars = ['A'..'Z'] ++ ['a'..'z'] ++ "._-+" ++ ['0'..'9']
|
||||
|
@ -100,12 +125,12 @@ slash = try (char '/' <* notFollowedBy (char '/')) <?> "slash"
|
|||
|
||||
-- | A path surrounded by angle brackets, indicating that it should be
|
||||
-- looked up in the NIX_PATH environment variable at evaluation.
|
||||
nixSPath :: Parser NExpr
|
||||
nixSPath = mkPath True <$> try (char '<' *> some (oneOf pathChars <|> slash) <* symbolic '>')
|
||||
nixSPath :: Parser NExprLoc
|
||||
nixSPath = annotateLocation1 $ mkPathF True <$> try (char '<' *> some (oneOf pathChars <|> slash) <* symbolic '>')
|
||||
<?> "spath"
|
||||
|
||||
nixPath :: Parser NExpr
|
||||
nixPath = token $ fmap (mkPath False) $ ((++)
|
||||
nixPath :: Parser NExprLoc
|
||||
nixPath = annotateLocation1 $ token $ fmap (mkPathF False) $ ((++)
|
||||
<$> (try ((++) <$> many (oneOf pathChars) <*> fmap (:[]) slash) <?> "path")
|
||||
<*> fmap concat
|
||||
( some (some (oneOf pathChars)
|
||||
|
@ -114,48 +139,49 @@ nixPath = token $ fmap (mkPath False) $ ((++)
|
|||
)
|
||||
<?> "path"
|
||||
|
||||
nixLet :: Parser NExpr
|
||||
nixLet = fmap Fix $ NLet
|
||||
nixLet :: Parser NExprLoc
|
||||
nixLet = annotateLocation1 $ NLet
|
||||
<$> (reserved "let" *> nixBinders)
|
||||
<*> (whiteSpace *> reserved "in" *> nixExpr)
|
||||
<*> (whiteSpace *> reserved "in" *> nixExprLoc)
|
||||
<?> "let"
|
||||
|
||||
nixIf :: Parser NExpr
|
||||
nixIf = fmap Fix $ NIf
|
||||
<$> (reserved "if" *> nixExpr)
|
||||
<*> (whiteSpace *> reserved "then" *> nixExpr)
|
||||
<*> (whiteSpace *> reserved "else" *> nixExpr)
|
||||
nixIf :: Parser NExprLoc
|
||||
nixIf = annotateLocation1 $ NIf
|
||||
<$> (reserved "if" *> nixExprLoc)
|
||||
<*> (whiteSpace *> reserved "then" *> nixExprLoc)
|
||||
<*> (whiteSpace *> reserved "else" *> nixExprLoc)
|
||||
<?> "if"
|
||||
|
||||
nixAssert :: Parser NExpr
|
||||
nixAssert = fmap Fix $ NAssert
|
||||
<$> (reserved "assert" *> nixExpr)
|
||||
<*> (semi *> nixExpr)
|
||||
nixAssert :: Parser NExprLoc
|
||||
nixAssert = annotateLocation1 $ NAssert
|
||||
<$> (reserved "assert" *> nixExprLoc)
|
||||
<*> (semi *> nixExprLoc)
|
||||
|
||||
nixWith :: Parser NExpr
|
||||
nixWith = fmap Fix $ NWith
|
||||
<$> (reserved "with" *> nixExpr)
|
||||
<*> (semi *> nixExpr)
|
||||
nixWith :: Parser NExprLoc
|
||||
nixWith = annotateLocation1 $ NWith
|
||||
<$> (reserved "with" *> nixExprLoc)
|
||||
<*> (semi *> nixExprLoc)
|
||||
|
||||
nixLambda :: Parser NExpr
|
||||
nixLambda = Fix <$> (NAbs <$> (try argExpr <?> "lambda arguments") <*> nixExpr) <?> "lambda"
|
||||
nixLambda :: Parser NExprLoc
|
||||
nixLambda = (nAbs <$> annotateLocation (try argExpr <?> "lambda arguments") <*> nixExprLoc) <?> "lambda"
|
||||
|
||||
nixStringExpr :: Parser NExpr
|
||||
nixStringExpr = Fix . NStr <$> nixString
|
||||
nixStringExpr :: Parser NExprLoc
|
||||
nixStringExpr = nStr <$> annotateLocation nixString
|
||||
|
||||
uriAfterColonC :: Parser Char
|
||||
uriAfterColonC = alphaNum <|> oneOf "%/?:@&=+$,-_.!~*'"
|
||||
|
||||
nixUri :: Parser NExpr
|
||||
nixUri = token $ fmap (mkUri . pack) $ (++)
|
||||
nixUri :: Parser NExprLoc
|
||||
nixUri = annotateLocation1 $ token $ fmap (mkUriF . pack) $ (++)
|
||||
<$> try ((++) <$> (scheme <* char ':') <*> fmap (\x -> [':',x]) uriAfterColonC)
|
||||
<*> many uriAfterColonC
|
||||
where
|
||||
scheme = (:) <$> letter <*> many (alphaNum <|> oneOf "+-.")
|
||||
|
||||
nixString :: Parser (NString NExpr)
|
||||
nixString :: Parser (NString NExprLoc)
|
||||
nixString = doubleQuoted <|> indented <?> "string"
|
||||
where
|
||||
doubleQuoted :: Parser (NString NExprLoc)
|
||||
doubleQuoted = DoubleQuoted . removePlainEmpty . mergePlain
|
||||
<$> (doubleQ *> many (stringChar doubleQ (void $ char '\\') doubleEscape)
|
||||
<* token doubleQ)
|
||||
|
@ -164,6 +190,7 @@ nixString = doubleQuoted <|> indented <?> "string"
|
|||
doubleQ = void $ char '"'
|
||||
doubleEscape = Plain . singleton <$> (char '\\' *> escapeCode)
|
||||
|
||||
indented :: Parser (NString NExprLoc)
|
||||
indented = stripIndent
|
||||
<$> (indentedQ *> many (stringChar indentedQ indentedQ indentedEscape)
|
||||
<* token indentedQ)
|
||||
|
@ -176,7 +203,7 @@ nixString = doubleQuoted <|> indented <?> "string"
|
|||
|
||||
stringChar end escStart esc
|
||||
= esc
|
||||
<|> Antiquoted <$> (antiStart *> nixExpr <* char '}') -- don't skip trailing space
|
||||
<|> Antiquoted <$> (antiStart *> nixExprLoc <* char '}') -- don't skip trailing space
|
||||
<|> Plain . singleton <$> char '$'
|
||||
<|> Plain . pack <$> some plainChar
|
||||
where plainChar = notFollowedBy (end <|> void (char '$') <|> escStart) *> anyChar
|
||||
|
@ -184,7 +211,7 @@ nixString = doubleQuoted <|> indented <?> "string"
|
|||
escapeCode = choice [ c <$ char e | (c,e) <- escapeCodes ] <|> anyChar
|
||||
|
||||
-- | Gets all of the arguments for a function.
|
||||
argExpr :: Parser (Params NExpr)
|
||||
argExpr :: Parser (Params NExprLoc)
|
||||
argExpr = choice [atLeft, onlyname, atRight] <* symbolic ':' where
|
||||
-- An argument not in curly braces. There's some potential ambiguity
|
||||
-- in the case of, for example `x:y`. Is it a lambda function `x: y`, or
|
||||
|
@ -213,45 +240,54 @@ argExpr = choice [atLeft, onlyname, atRight] <* symbolic ':' where
|
|||
|
||||
-- Collects the parameters within curly braces. Returns the parameters and
|
||||
-- a boolean indicating if the parameters are variadic.
|
||||
getParams :: Parser ([(Text, Maybe NExpr)], Bool)
|
||||
getParams :: Parser ([(Text, Maybe NExprLoc)], Bool)
|
||||
getParams = go [] where
|
||||
-- Attempt to parse `...`. If this succeeds, stop and return True.
|
||||
-- Otherwise, attempt to parse an argument, optionally with a
|
||||
-- default. If this fails, then return what has been accumulated
|
||||
-- so far.
|
||||
go acc = (token (string "...") >> return (acc, True)) <|> getMore acc
|
||||
getMore acc = do
|
||||
getMore acc =
|
||||
-- Could be nothing, in which just return what we have so far.
|
||||
option (acc, False) $ do
|
||||
-- Get an argument name and an optional default.
|
||||
pair <- liftA2 (,) identifier (optional $ symbolic '?' *> nixExpr)
|
||||
pair <- liftA2 (,) identifier (optional $ symbolic '?' *> nixExprLoc)
|
||||
-- Either return this, or attempt to get a comma and restart.
|
||||
option (acc ++ [pair], False) $ symbolic ',' >> go (acc ++ [pair])
|
||||
|
||||
nixBinders :: Parser [Binding NExpr]
|
||||
nixBinders :: Parser [Binding NExprLoc]
|
||||
nixBinders = (inherit <|> namedVar) `endBy` symbolic ';' where
|
||||
inherit = Inherit <$> (reserved "inherit" *> optional scope)
|
||||
<*> many (keyName)
|
||||
<*> many keyName
|
||||
<?> "inherited binding"
|
||||
namedVar = NamedVar <$> nixSelector <*> (symbolic '=' *> nixExpr)
|
||||
namedVar = NamedVar <$> (annotated <$> nixSelector) <*> (symbolic '=' *> nixExprLoc)
|
||||
<?> "variable binding"
|
||||
scope = parens nixExpr <?> "inherit scope"
|
||||
scope = parens nixExprLoc <?> "inherit scope"
|
||||
|
||||
keyName :: Parser (NKeyName NExpr)
|
||||
keyName :: Parser (NKeyName NExprLoc)
|
||||
keyName = dynamicKey <|> staticKey where
|
||||
staticKey = StaticKey <$> identifier
|
||||
dynamicKey = DynamicKey <$> nixAntiquoted nixString
|
||||
|
||||
nixSet :: Parser NExpr
|
||||
nixSet = Fix <$> (isRec <*> braces nixBinders) <?> "set" where
|
||||
nixSet :: Parser NExprLoc
|
||||
nixSet = annotateLocation1 $ (isRec <*> braces nixBinders) <?> "set" where
|
||||
isRec = (try (reserved "rec" *> pure NRecSet) <?> "recursive set")
|
||||
<|> pure NSet
|
||||
|
||||
parseNixFile :: MonadIO m => FilePath -> m (Result NExpr)
|
||||
parseNixFile = parseFromFileEx $ nixExpr <* eof
|
||||
|
||||
parseNixFileLoc :: MonadIO m => FilePath -> m (Result NExprLoc)
|
||||
parseNixFileLoc = parseFromFileEx $ nixExprLoc <* eof
|
||||
|
||||
parseNixString :: String -> Result NExpr
|
||||
parseNixString = parseFromString $ nixExpr <* eof
|
||||
|
||||
parseNixStringLoc :: String -> Result NExprLoc
|
||||
parseNixStringLoc = parseFromString $ nixExprLoc <* eof
|
||||
|
||||
parseNixText :: Text -> Result NExpr
|
||||
parseNixText = parseNixString . unpack
|
||||
|
||||
parseNixTextLoc :: Text -> Result NExprLoc
|
||||
parseNixTextLoc = parseNixStringLoc . unpack
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
module Nix.Parser.Library ( module Nix.Parser.Library, module X) where
|
||||
module Nix.Parser.Library
|
||||
( module Nix.Parser.Library
|
||||
, module X
|
||||
, Trifecta.Delta(..)
|
||||
) where
|
||||
|
||||
import Prelude
|
||||
import Control.Applicative
|
||||
|
@ -31,7 +35,7 @@ import Text.Trifecta as X (Result(..))
|
|||
#endif
|
||||
|
||||
newtype NixParser p a = NixParser { runNixParser :: p a }
|
||||
deriving (Functor, Applicative, Alternative, Monad, MonadPlus, Parsing, CharParsing, LookAheadParsing)
|
||||
deriving (Functor, Applicative, Alternative, Monad, MonadPlus, Parsing, CharParsing, LookAheadParsing, Trifecta.DeltaParsing)
|
||||
|
||||
instance TokenParsing p => TokenParsing (NixParser p) where
|
||||
someSpace = NixParser $ buildSomeSpaceParser someSpace commentStyle
|
||||
|
@ -104,6 +108,7 @@ someTill p end = go
|
|||
--------------------------------------------------------------------------------
|
||||
parseFromFileEx :: MonadIO m => Parser a -> FilePath -> m (Result a)
|
||||
parseFromString :: Parser a -> String -> Result a
|
||||
position :: Parser Trifecta.Delta
|
||||
|
||||
#if USE_PARSEC
|
||||
data Result a = Success a
|
||||
|
@ -118,6 +123,8 @@ parseFromFileEx p path =
|
|||
|
||||
parseFromString p = either (Failure . text . show) Success . Parsec.parse (runNixParser p) "<string>" . pack
|
||||
|
||||
position = error "position not implemented for Parsec parser"
|
||||
|
||||
#else
|
||||
|
||||
type Parser = NixParser Trifecta.Parser
|
||||
|
@ -125,4 +132,7 @@ type Parser = NixParser Trifecta.Parser
|
|||
parseFromFileEx p = Trifecta.parseFromFileEx (runNixParser p)
|
||||
|
||||
parseFromString p = Trifecta.parseString (runNixParser p) (Trifecta.Directed "<string>" 0 0 0 0)
|
||||
|
||||
position = Trifecta.position
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
Name: hnix
|
||||
Version: 0.3.0
|
||||
Version: 0.3.1
|
||||
Synopsis: Haskell implementation of the Nix language
|
||||
Description:
|
||||
Haskell implementation of the Nix language.
|
||||
|
@ -32,6 +32,7 @@ Library
|
|||
Other-modules:
|
||||
Nix.Parser.Library
|
||||
Nix.Expr.Types
|
||||
Nix.Expr.Types.Annotated
|
||||
Nix.Expr.Shorthands
|
||||
Default-extensions:
|
||||
DataKinds
|
||||
|
@ -59,6 +60,7 @@ Library
|
|||
, unordered-containers
|
||||
, data-fix
|
||||
, deepseq
|
||||
, semigroups >= 0.18 && < 0.19
|
||||
if flag(parsec)
|
||||
Cpp-options: -DUSE_PARSEC
|
||||
Build-depends: parsec
|
||||
|
|
17
project.nix
17
project.nix
|
@ -1,5 +1,5 @@
|
|||
{ mkDerivation, ansi-wl-pprint, base, containers, data-fix, parsers
|
||||
, stdenv, tasty, tasty-hunit, tasty-th, text, transformers
|
||||
{ mkDerivation, ansi-wl-pprint, base, containers, data-fix, deepseq
|
||||
, parsers, stdenv, tasty, tasty-hunit, tasty-th, text, transformers
|
||||
, trifecta, unordered-containers, cabal-install, criterion, pkgs
|
||||
}:
|
||||
|
||||
|
@ -10,17 +10,20 @@ in
|
|||
|
||||
mkDerivation {
|
||||
pname = "hnix";
|
||||
version = "0.3.0";
|
||||
version = "0.3.1";
|
||||
src = let
|
||||
notNamed = list: name: !(elem (baseNameOf name) list);
|
||||
in filterSource (n: _: notNamed [".git" "dist" "benchmarks"] n) ./.;
|
||||
isLibrary = true;
|
||||
isExecutable = true;
|
||||
buildDepends = [
|
||||
ansi-wl-pprint base containers data-fix parsers text transformers
|
||||
trifecta unordered-containers cabal-install criterion
|
||||
libraryHaskellDepends = [
|
||||
ansi-wl-pprint base containers data-fix deepseq parsers text
|
||||
transformers trifecta unordered-containers cabal-install criterion
|
||||
];
|
||||
testDepends = [
|
||||
executableHaskellDepends = [
|
||||
ansi-wl-pprint base containers data-fix deepseq
|
||||
];
|
||||
testHaskellDepends = [
|
||||
base containers data-fix tasty tasty-hunit tasty-th text
|
||||
];
|
||||
homepage = "http://github.com/jwiegley/hnix";
|
||||
|
|
Loading…
Reference in a new issue