split into more modules, rename some things, add docs

This commit is contained in:
Allen Nelson 2016-01-23 15:20:33 -06:00
parent f60f12f9a0
commit d3a1e7207f
8 changed files with 539 additions and 10 deletions

View file

@ -13,10 +13,50 @@ import qualified Data.Map as Map
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Traversable as T
import Nix.Types
import Data.Typeable (Typeable)
import GHC.Generics
import Nix.Pretty (atomText)
import Nix.StringOperations (runAntiquoted)
import Nix.Expr
import Prelude hiding (mapM, sequence)
<<<<<<< f60f12f9a023dac427afc7194baf610b149f3ea0
buildArgument :: Formals (NValue m) -> NValue m -> NValue m
=======
-- | An 'NValue' is the most reduced form of an 'NExpr' after evaluation
-- is completed.
data NValueF r
= NVConstant NAtom
| NVStr Text
| NVList [r]
| NVSet (Map.Map Text r)
| NVFunction (Formals r) (NValue -> IO r)
deriving (Generic, Typeable, Functor)
instance Show f => Show (NValueF f) where
showsPrec = flip go where
go (NVConstant atom) = showsCon1 "NVConstant" atom
go (NVStr text) = showsCon1 "NVStr" text
go (NVList list) = showsCon1 "NVList" list
go (NVSet attrs) = showsCon1 "NVSet" attrs
go (NVFunction r _) = showsCon1 "NVFunction" r
showsCon1 :: Show a => String -> a -> Int -> String -> String
showsCon1 con a d = showParen (d > 10) $
showString (con ++ " ") . showsPrec 11 a
type NValue = Fix NValueF
valueText :: NValue -> Text
valueText = cata phi where
phi (NVConstant a) = atomText a
phi (NVStr t) = t
phi (NVList _) = error "Cannot coerce a list to a string"
phi (NVSet _) = error "Cannot coerce a set to a string"
phi (NVFunction _ _) = error "Cannot coerce a function to a string"
buildArgument :: Formals NValue -> NValue -> NValue
>>>>>>> split into more modules, rename some things, add docs
buildArgument paramSpec arg = either error (Fix . NVSet) $ case paramSpec of
FormalName name -> return $ Map.singleton name arg
FormalSet s Nothing -> lookupParamSet s
@ -40,6 +80,10 @@ evalExpr = cata phi
where err = error ("Undefined variable: " ++ show var)
phi (NConstant x) = const $ return $ Fix $ NVConstant x
phi (NStr str) = fmap (Fix . NVStr) . flip evalString str
phi (NOper _x) = error "Operators are not yet defined"
phi (NPath _ _) = error "Path expressions are not yet supported"
phi (NSelect _x _attr _or) = error "Select expressions are not yet supported"
phi (NHasAttr _x _attr) = error "Has attr expressions are not yet supported"
phi (NOper x) = \env -> case x of
NUnary op arg -> arg env >>= \case
@ -186,7 +230,7 @@ evalBinds allowDynamic env xs = buildResult <$> sequence (concatMap go xs) where
go (NamedVar x y) = [liftM2 (,) (evalSelector allowDynamic env x) (y env)]
go _ = [] -- HACK! But who cares right now
evalSelector :: Monad m => Bool -> NValue m -> NSelector (NValue m -> m (NValue m)) -> m [Text]
evalSelector :: Monad m => Bool -> NValue m -> NAttrPath (NValue m -> m (NValue m)) -> m [Text]
evalSelector dyn env = mapM evalKeyName where
evalKeyName (StaticKey k) = return k
evalKeyName (DynamicKey k)

294
Nix/Expr.hs Normal file
View file

@ -0,0 +1,294 @@
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}
-- | The nix expression type and supporting types.
module Nix.Expr where
import Control.Monad hiding (forM_, mapM, sequence)
import Data.Data
import Data.Fix
import Data.Foldable
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Monoid
import Data.Text (Text, pack)
import Data.Traversable
import GHC.Exts
import GHC.Generics
import Prelude hiding (readFile, concat, concatMap, elem, mapM,
sequence, minimum, foldr)
-- | Atoms are values that evaluate to themselves. This means that
-- they appear in both the parsed AST (in the form of literals) and
-- the evaluated form.
data NAtom
-- | An integer. The c nix implementation currently only supports
-- integers that fit in the range of 'Int64'.
= NInt Integer
-- | Booleans.
| NBool Bool
-- | Null values. There's only one of this variant.
| NNull
deriving (Eq, Ord, Generic, Typeable, Data, Show)
data NSetBind = Rec | NonRec
deriving (Ord, Eq, Generic, Typeable, Data, Show)
-- | A single line of the bindings section of a let expression or of
-- a set.
data Binding r
= NamedVar (NAttrPath r) r
| Inherit (Maybe r) [NAttrPath r]
deriving (Typeable, Data, Ord, Eq, Functor, Show)
-- | For functions which are called with a set as an argument.
data FormalParamSet r
= FixedParamSet (Map Text (Maybe r))
-- ^ Parameters for a function that expects an attribute set. The values
-- are @Just@ if they specify a default argument. For a fixed set, no
-- arguments beyond what is specified in the map may be given.
| VariadicParamSet (Map Text (Maybe r))
-- ^ Same as the 'FixedParamSet', but extra arguments are allowed.
deriving (Eq, Ord, Generic, Typeable, Data, Functor, Show,
Foldable, Traversable)
-- | @Formals@ represents all the ways the formal parameters to a
-- function can be represented.
data Formals r
= FormalName Text
-- ^ For functions with a single named argument, such as @x: x + 1@.
| FormalSet (FormalParamSet r) (Maybe Text)
-- ^ For functions that expect an attribute set argument, and unpack values
-- from it. For example, @{x, y}: x + y@.
deriving (Ord, Eq, Generic, Typeable, Data, Functor, Show,
Foldable, Traversable)
-- | For the two different kinds of strings.
data StringKind = DoubleQuoted | Indented
deriving (Eq, Ord, Generic, Typeable, Data, Show)
-- | An 'NString' is a list of things that are either a plain string
-- or an antiquoted expression. After the antiquotes have been evaluated,
-- the final string is constructed by concating all the parts.
data NString r = NString StringKind [Antiquoted Text r] | NUri Text
deriving (Eq, Ord, Generic, Typeable, Data, Functor, Show)
-- | A 'KeyName' is something that can appear at the right side of an
-- equals sign.
-- For example, @a@ is a 'KeyName' in @{ a = 3; }@, @let a = 3;
-- in ...@, @{}.a@ or @{} ? a@.
--
-- Nix supports both static keynames (just an identifier) and dynamic
-- identifiers. Dynamic identifiers can be either a string (e.g.:
-- @{ "a" = 3; }@) or an antiquotation (e.g.: @let a = "example";
-- in { ${a} = 3; }.example@).
--
-- Note: There are some places where a dynamic keyname is not allowed.
-- In particular, those include:
--
-- * The RHS of a @binding@ inside @let@: @let ${"a"} = 3; in ...@
-- produces a syntax error.
-- * The attribute names of an 'inherit': @inherit ${"a"};@ is forbidden.
--
-- Note: In Nix, a simple string without antiquotes such as @"foo"@ is
-- allowed even if the context requires a static keyname, but the
-- parser still considers it a 'DynamicKey' for simplicity.
data NKeyName r
= DynamicKey (Antiquoted (NString r) r)
| StaticKey Text
deriving (Eq, Ord, Generic, Typeable, Data, Show)
-- | Deriving this instance automatically is not possible because @r@
-- occurs not only as last argument in @Antiquoted (NString r) r@
instance Functor NKeyName where
fmap f (DynamicKey (Plain str)) = DynamicKey . Plain $ fmap f str
fmap f (DynamicKey (Antiquoted e)) = DynamicKey . Antiquoted $ f e
fmap _ (StaticKey key) = StaticKey key
-- | A selector (for example in a @let@ or an attribute set) is made up
-- of strung-together key names.
type NAttrPath r = [NKeyName r]
-- | A functor-ized nix expression type, which lets us do things like traverse
-- expressions and map functions over them. The actual 'NExpr' type is defined
-- below.
data NExprF r
= NConstant NAtom
-- ^ Constants: ints, bools, and null.
| NStr (NString r)
-- ^ A string, with interpolated expressions.
| NList [r]
-- ^ A list literal.
| NSet NSetBind [Binding r]
-- ^ An attribute set literal, possibly recursive.
| NAbs (Formals r) r
-- ^ A lambda abstraction.
| NPath Bool FilePath
-- ^ A path expression, which is evaluated to a store path. The boolean
-- argument of 'NPath' is 'True' if the path refers to something in the
-- Nix search path. For example, @<nixpkgs/pkgs>@ is represented by
-- @NPath True "nixpkgs/pkgs"@, while @foo/bar@ is represented by @NPath
-- False "foo/bar@.
| NOper (NOperF r)
-- ^ Binary or unary operators.
| NSelect r (NAttrPath r) (Maybe r)
-- ^ Dot-reference into an attribute set, optionally providing an
-- alternative if the key doesn't exist.
| NHasAttr r (NAttrPath r)
-- ^ Ask if a set contains a given attribute path.
| NApp r r
-- ^ Apply a function to an argument.
| NSym Text
-- ^ A variable. For example, in the expression @f a@, @f@ is represented
-- as @NSym "f"@ and @a@ as @NSym "a"@.
| NLet [Binding r] r
-- ^ Evaluate the second argument after introducing the bindings.
| NIf r r r
-- ^ If-then-else statement.
| NWith r r
-- ^ Evaluate an attribute set, bring its bindings into scope, and
-- evaluate the second argument.
| NAssert r r
-- ^ Assert that the first returns true before evaluating the second.
deriving (Ord, Eq, Generic, Typeable, Data, Functor, Show)
type NExpr = Fix NExprF
-- | 'Antiquoted' represents an expression that is either
-- antiquoted (surrounded by ${...}) or plain (not antiquoted).
data Antiquoted v r = Plain v | Antiquoted r
deriving (Ord, Eq, Generic, Typeable, Data, Functor, Show)
-- | For the the 'IsString' instance, we use a plain doublequoted string.
instance IsString (NString r) where
fromString "" = NString DoubleQuoted []
fromString x = NString DoubleQuoted . (:[]) . Plain . pack $ x
-- | Operator expressions are unary or binary.
data NOperF r
= NUnary NUnaryOp r
| NBinary NBinaryOp r r
deriving (Eq, Ord, Generic, Typeable, Data, Functor, Show)
-- | There are two unary operations: logical not and integer negation.
data NUnaryOp = NNeg | NNot
deriving (Eq, Ord, Generic, Typeable, Data, Show)
data NBinaryOp
= NEq -- ^ Equality (==)
| NNEq -- ^ Inequality (!=)
| NLt -- ^ Less than (<)
| NLte -- ^ Less than or equal (<=)
| NGt -- ^ Greater than (>)
| NGte -- ^ Greater than or equal (>=)
| NAnd -- ^ Logical and (&&)
| NOr -- ^ Logical or (||)
| NImpl -- ^ Logical implication (->)
| NUpdate -- ^ Joining two attribut sets (//)
| NPlus -- ^ Addition (+)
| NMinus -- ^ Subtraction (-)
| NMult -- ^ Multiplication (*)
| NDiv -- ^ Division (/)
| NConcat -- ^ List concatenation (++)
deriving (Eq, Ord, Generic, Typeable, Data, Show)
mkInt :: Integer -> NExpr
mkInt = Fix . NConstant . NInt
mkStr :: StringKind -> Text -> NExpr
mkStr kind x = Fix . NStr . NString kind $ if x == ""
then []
else [Plain x]
mkUri :: Text -> NExpr
mkUri = Fix . NStr . NUri
mkPath :: Bool -> FilePath -> NExpr
mkPath b = Fix . NPath b
-- | Make a path expression which pulls from the NIX_PATH env variable.
mkEnvPath :: FilePath -> NExpr
mkEnvPath = mkPath True
-- | Make a path expression which references a relative path.
mkRelPath :: FilePath -> NExpr
mkRelPath = mkPath False
mkSym :: Text -> NExpr
mkSym = Fix . NSym
mkSelector :: Text -> NAttrPath NExpr
mkSelector = (:[]) . StaticKey
mkBool :: Bool -> NExpr
mkBool = Fix . NConstant . NBool
mkNull :: NExpr
mkNull = Fix (NConstant NNull)
mkOper :: NUnaryOp -> NExpr -> NExpr
mkOper op = Fix . NOper . NUnary op
mkOper2 :: NBinaryOp -> NExpr -> NExpr -> NExpr
mkOper2 op a = Fix . NOper . NBinary op a
mkFormalSet :: [(Text, Maybe NExpr)] -> Formals NExpr
mkFormalSet = mkFixedParamSet
mkFixedParamSet :: [(Text, Maybe NExpr)] -> Formals NExpr
mkFixedParamSet ps = FormalSet (FixedParamSet $ Map.fromList ps) Nothing
mkVariadicParamSet :: [(Text, Maybe NExpr)] -> Formals NExpr
mkVariadicParamSet ps = FormalSet (VariadicParamSet $ Map.fromList ps) Nothing
mkApp :: NExpr -> NExpr -> NExpr
mkApp e = Fix . NApp e
mkRecSet :: [Binding NExpr] -> NExpr
mkRecSet = Fix . NSet Rec
mkNonRecSet :: [Binding NExpr] -> NExpr
mkNonRecSet = Fix . NSet NonRec
mkLet :: [Binding NExpr] -> NExpr -> NExpr
mkLet bs = Fix . NLet bs
mkList :: [NExpr] -> NExpr
mkList = Fix . NList
mkWith :: NExpr -> NExpr -> NExpr
mkWith e = Fix . NWith e
mkAssert :: NExpr -> NExpr -> NExpr
mkAssert e = Fix . NWith e
mkIf :: NExpr -> NExpr -> NExpr -> NExpr
mkIf e1 e2 = Fix . NIf e1 e2
mkFunction :: Formals NExpr -> NExpr -> NExpr
mkFunction params = Fix . NAbs params
-- | Shorthand for producing a binding of a name to an expression.
bindTo :: Text -> NExpr -> Binding NExpr
bindTo name val = NamedVar (mkSelector name) val
-- | Append a list of bindings to a set or let expression.
-- For example, adding `[a = 1, b = 2]` to `let c = 3; in 4` produces
-- `let a = 1; b = 2; c = 3; in 4`.
appendBindings :: [Binding NExpr] -> NExpr -> NExpr
appendBindings newBindings (Fix e) = case e of
NLet bindings e' -> Fix $ NLet (bindings <> newBindings) e'
NSet bindType bindings -> Fix $ NSet bindType (bindings <> newBindings)
_ -> error "Can only append bindings to a set or a let"
-- | Applies a transformation to the body of a nix function.
modifyFunctionBody :: (NExpr -> NExpr) -> NExpr -> NExpr
modifyFunctionBody f (Fix e) = case e of
NAbs params body -> Fix $ NAbs params (f body)
_ -> error "Not a function"

View file

@ -16,7 +16,9 @@ import Data.Foldable hiding (concat)
import qualified Data.Map as Map
import Data.Text hiding (head, map, foldl1', foldl', concat)
import Nix.Parser.Library
import Nix.Types
import Nix.Parser.Operators
import Nix.Expr
import Nix.StringOperations
import Prelude hiding (elem)
-- | The lexer for this parser is defined in 'Nix.Parser.Library'.
@ -45,7 +47,7 @@ selDot :: Parser ()
selDot = try (char '.' *> notFollowedBy (("path" :: String) <$ nixPath)) *> whiteSpace
<?> "."
nixSelector :: Parser (NSelector NExpr)
nixSelector :: Parser (NAttrPath NExpr)
nixSelector = keyName `sepBy1` selDot where
nixSelect :: Parser NExpr -> Parser NExpr

81
Nix/Parser/Operators.hs Normal file
View file

@ -0,0 +1,81 @@
module Nix.Parser.Operators where
import Data.Data (Data(..))
import Data.Foldable (concat)
import qualified Data.Map as Map
import Data.Maybe (catMaybes)
import Data.Typeable (Typeable)
import GHC.Exts
import GHC.Generics
import Nix.Expr
import Prelude hiding (readFile, concat, concatMap, elem, mapM,
sequence, minimum, foldr)
data NSpecialOp = NHasAttrOp | NSelectOp | NAppOp
deriving (Eq, Ord, Generic, Typeable, Data, Show)
data NAssoc = NAssocNone | NAssocLeft | NAssocRight
deriving (Eq, Ord, Generic, Typeable, Data, Show)
data NOperatorDef
= NUnaryDef String NUnaryOp
| NBinaryDef NAssoc [(String, NBinaryOp)]
deriving (Eq, Ord, Generic, Typeable, Data, Show)
nixOperators :: [Either NSpecialOp NOperatorDef]
nixOperators =
[ Left NSelectOp
, Left NAppOp
, Right $ NUnaryDef "-" NNeg
, Left NHasAttrOp
] ++ map Right
[ NBinaryDef NAssocRight [("++", NConcat)]
, NBinaryDef NAssocLeft [("*", NMult), ("/", NDiv)]
, NBinaryDef NAssocLeft [("+", NPlus), ("-", NMinus)]
, NUnaryDef "!" NNot
, NBinaryDef NAssocRight [("//", NUpdate)]
, NBinaryDef NAssocLeft [("<", NLt), (">", NGt), ("<=", NLte), (">=", NGte)]
, NBinaryDef NAssocNone [("==", NEq), ("!=", NNEq)]
, NBinaryDef NAssocLeft [("&&", NAnd)]
, NBinaryDef NAssocLeft [("||", NOr)]
, NBinaryDef NAssocNone [("->", NImpl)]
]
data OperatorInfo = OperatorInfo
{ precedence :: Int
, associativity :: NAssoc
, operatorName :: String
} deriving (Eq, Ord, Generic, Typeable, Data, Show)
getUnaryOperator :: NUnaryOp -> OperatorInfo
getUnaryOperator = (m Map.!) where
m = Map.fromList . concat . zipWith buildEntry [1..] . reverse $
nixOperators
buildEntry i = \case
Right (NUnaryDef name op) -> [(op, OperatorInfo i NAssocNone name)]
_ -> []
getBinaryOperator :: NBinaryOp -> OperatorInfo
getBinaryOperator = (m Map.!) where
m = Map.fromList . concat . zipWith buildEntry [1..] . reverse $
nixOperators
buildEntry i = \case
Right (NBinaryDef assoc ops) -> do
[(op, OperatorInfo i assoc name) | (name,op) <- ops]
_ -> []
getSpecialOperatorPrec :: NSpecialOp -> Int
getSpecialOperatorPrec = (m Map.!) where
m = Map.fromList . catMaybes . zipWith buildEntry [1..] . reverse $
nixOperators
buildEntry _ (Right _) = Nothing
buildEntry i (Left op) = Just (op, i)
selectOp :: OperatorInfo
selectOp = OperatorInfo (getSpecialOperatorPrec NSelectOp) NAssocLeft "."
hasAttrOp :: OperatorInfo
hasAttrOp = OperatorInfo (getSpecialOperatorPrec NHasAttrOp) NAssocLeft "?"
appOp :: OperatorInfo
appOp = OperatorInfo (getSpecialOperatorPrec NAppOp) NAssocLeft " "

View file

@ -5,9 +5,12 @@ import Prelude hiding ((<$>))
import Data.Fix
import Data.Map (toList)
import Data.Maybe (isJust)
import Data.Text (Text, unpack, replace, strip)
import Nix.Types
import Data.Text (Text, pack, unpack, replace, strip)
import Data.List (isPrefixOf)
import Nix.Expr
import Nix.Parser.Library (reservedNames)
import Nix.Parser.Operators
import Nix.StringOperations
import Text.PrettyPrint.ANSI.Leijen
import qualified Data.Text as Text
@ -98,7 +101,7 @@ prettyKeyName (StaticKey key)
prettyKeyName (StaticKey key) = text . unpack $ key
prettyKeyName (DynamicKey key) = runAntiquoted prettyString withoutParens key
prettySelector :: NSelector NixDoc -> Doc
prettySelector :: NAttrPath NixDoc -> Doc
prettySelector = hcat . punctuate dot . map prettyKeyName
prettySetArg :: (Text, Maybe NixDoc) -> Doc
@ -120,6 +123,12 @@ prettyOper (NUnary op r1) =
NixDoc (text (operatorName opInfo) <> wrapParens opInfo r1) opInfo
where opInfo = getUnaryOperator op
-- | Translate an atom into its nix representation.
atomText :: NAtom -> Text
atomText (NInt i) = pack (show i)
atomText (NBool b) = if b then "true" else "false"
atomText NNull = "null"
prettyAtom :: NAtom -> NixDoc
prettyAtom atom = simpleExpr $ text $ unpack $ atomText atom
@ -144,7 +153,17 @@ prettyNix = withoutParens . cata phi where
= NixDoc (wrapParens hasAttrOp r <+> text "?" <+> prettySelector attr) hasAttrOp
phi (NApp fun arg)
= NixDoc (wrapParens appOp fun <+> wrapParens appOpNonAssoc arg) appOp
phi (NPath isFromEnv p)
| isFromEnv = simpleExpr $ text ("<" ++ p ++ ">")
-- If it's not an absolute path, we need to prefix with ./
| otherwise = simpleExpr $ text $ case p of
"./" -> "./."
"../" -> "../."
".." -> "../."
txt | "/" `isPrefixOf` txt -> txt
| "./" `isPrefixOf` txt -> txt
| "../" `isPrefixOf` txt -> txt
| otherwise -> "./" ++ txt
phi (NSym name) = simpleExpr $ text (unpack name)
phi (NLet binds body) = leastPrecedence $ group $ nest 2 $
vsep (text "let" : map prettyBind binds) <$> text "in" <+> withoutParens body

87
Nix/StringOperations.hs Normal file
View file

@ -0,0 +1,87 @@
-- | Functions for manipulating nix strings.
module Nix.StringOperations where
import Nix.Expr
import Data.List (intercalate)
import Data.Monoid ((<>))
import Data.Text (Text)
import qualified Data.Text as T
import Prelude hiding (elem)
import Data.Tuple (swap)
-- | Merge adjacent 'Plain' values with 'mappend'.
mergePlain :: Monoid v => [Antiquoted v r] -> [Antiquoted v r]
mergePlain [] = []
mergePlain (Plain a: Plain b: xs) = mergePlain (Plain (a <> b) : xs)
mergePlain (x:xs) = x : mergePlain xs
-- | Remove 'Plain' values equal to 'mempty', as they don't have any
-- informational content.
removePlainEmpty :: (Eq v, Monoid v) => [Antiquoted v r] -> [Antiquoted v r]
removePlainEmpty = filter f where
f (Plain x) = x /= mempty
f _ = True
-- | Equivalent to case splitting on 'Antiquoted' strings.
runAntiquoted :: (v -> a) -> (r -> a) -> Antiquoted v r -> a
runAntiquoted f _ (Plain v) = f v
runAntiquoted _ f (Antiquoted r) = f r
-- | Split a stream representing a string with antiquotes on line breaks.
splitLines :: [Antiquoted Text r] -> [[Antiquoted Text r]]
splitLines = uncurry (flip (:)) . go where
go (Plain t : xs) = (Plain l :) <$> foldr f (go xs) ls where
(l : ls) = T.split (=='\n') t
f prefix (finished, current) = ((Plain prefix : current) : finished, [])
go (Antiquoted a : xs) = (Antiquoted a :) <$> go xs
go [] = ([],[])
-- | Join a stream of strings containing antiquotes again. This is the inverse
-- of 'splitLines'.
unsplitLines :: [[Antiquoted Text r]] -> [Antiquoted Text r]
unsplitLines = intercalate [Plain "\n"]
-- | Form an indented string by stripping spaces equal to the minimal indent.
stripIndent :: [Antiquoted Text r] -> NString r
stripIndent [] = NString Indented []
stripIndent xs =
NString Indented . removePlainEmpty . mergePlain . unsplitLines $ ls'
where
ls = stripEmptyOpening $ splitLines xs
ls' = map (dropSpaces minIndent) ls
minIndent = case stripEmptyLines ls of
[] -> 0
nonEmptyLs -> minimum $ map (countSpaces . mergePlain) nonEmptyLs
stripEmptyLines = filter $ \case
[Plain t] -> not $ T.null $ T.strip t
_ -> True
stripEmptyOpening ([Plain t]:ts) | T.null (T.strip t) = ts
stripEmptyOpening ts = ts
countSpaces (Antiquoted _:_) = 0
countSpaces (Plain t : _) = T.length . T.takeWhile (== ' ') $ t
countSpaces [] = 0
dropSpaces 0 x = x
dropSpaces n (Plain t : cs) = Plain (T.drop n t) : cs
dropSpaces _ _ = error "stripIndent: impossible"
escapeCodes :: [(Char, Char)]
escapeCodes =
[ ('\n', 'n' )
, ('\r', 'r' )
, ('\t', 't' )
, ('\\', '\\')
, ('$' , '$' )
, ('"', '"')
]
fromEscapeCode :: Char -> Maybe Char
fromEscapeCode = (`lookup` map swap escapeCodes)
toEscapeCode :: Char -> Maybe Char
toEscapeCode = (`lookup` escapeCodes)

View file

@ -24,7 +24,7 @@ Library
Exposed-modules:
Nix.Eval
Nix.Parser
Nix.Types
Nix.Expr
Nix.Pretty
Other-modules:
Nix.Parser.Library
@ -52,6 +52,7 @@ Library
, parsers >= 0.10
, unordered-containers
, data-fix
, deepseq
if flag(parsec)
Cpp-options: -DUSE_PARSEC
Build-depends: parsec
@ -84,6 +85,7 @@ Executable hnix
, containers
, ansi-wl-pprint
, data-fix
, deepseq
Ghc-options: -Wall
Test-suite hnix-tests

View file

@ -10,7 +10,7 @@ import Test.Tasty.TH
import qualified Data.Map as Map
import Nix.Types
import Nix.Expr
import Nix.Parser
case_constant_int :: Assertion