Skip to content

Commit 6417872

Browse files
Language.Nix.Identifier: quote Identifiers that are Nix keywords
This relates to #164, as there are packages like `assert` which clash with Nix keywords. However, this does not actually provide us with a solution since quoting isn't possible in some contexts (e.g. function arguments).
1 parent 5ee5f4c commit 6417872

File tree

4 files changed

+38
-6
lines changed

4 files changed

+38
-6
lines changed

language-nix/CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Revision History for language-nix
22

3-
## unreleased
3+
## 2.3.0 (unreleased)
44

5+
* `Language.Nix.Identifier` now exports `nixKeywords` which lists
6+
keywords in Nix that are parseable as simple identifiers, but have
7+
special meaning, like `assert` and `with`. Consequently, they can't
8+
be used as (simple) identifiers in Nix code.
9+
10+
`quote`, `needsQuoting` and `Pretty` will take this list into account
11+
and quote such identifiers. However, `HasParser` will _not_ reject them
12+
even if they are unquoted.
513
* Add an hspec/QuickCheck based test suite.

language-nix/language-nix.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: language-nix
2-
version: 2.2.0
2+
version: 2.3.0
33
synopsis: Data types and functions to represent the Nix language
44
description: Data types and useful functions to represent and manipulate the Nix
55
language.

language-nix/src/Language/Nix/Identifier.hs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
{-# LANGUAGE TemplateHaskell #-}
55

66
module Language.Nix.Identifier
7-
( Identifier, ident, quote, needsQuoting
7+
( Identifier, ident, quote, needsQuoting, nixKeywords
88
, parseSimpleIdentifier, parseQuotedIdentifier
9+
-- TODO: why do we expose quote?
910
)
1011
where
1112

@@ -44,6 +45,7 @@ import Text.PrettyPrint.HughesPJClass as PP
4445
-- __Warning__: Identifiers /may not/ contain @\'\\0\'@, but this is not
4546
-- checked during construction!
4647
--
48+
-- See also <https://nix.dev/manual/nix/2.30/language/identifiers.html>.
4749
declareLenses [d| newtype Identifier = Identifier { ident :: String }
4850
deriving (Show, Eq, Ord, IsString, Generic)
4951
|]
@@ -75,10 +77,17 @@ instance CoArbitrary Identifier
7577
instance Pretty Identifier where
7678
pPrint = view (ident . to quote . to text)
7779

80+
-- | Note that this parser is more lenient than Nix w.r.t. simple identifiers,
81+
-- since it will accept 'nixKeywords'.
7882
instance HasParser Identifier where
7983
parser = parseQuotedIdentifier <|> parseSimpleIdentifier
8084

8185
-- | Parsec parser for simple identifiers, i.e. those that don't need quoting.
86+
-- The parser is equivalent to the regular expression @^[a-zA-Z_][a-zA-Z0-9_'-]*$@
87+
-- which the Nix parser uses.
88+
--
89+
-- Note that this parser will accept keywords which would not be parsed as
90+
-- identifiers by Nix, see 'nixKeywords'.
8291
parseSimpleIdentifier :: CharParser st tok m Identifier
8392
parseSimpleIdentifier = do
8493
c <- satisfy (\x -> x == '_' || isAlpha x)
@@ -104,10 +113,19 @@ parseQuotedIdentifier = Identifier <$> qstring
104113
return [c1,c2]
105114

106115
-- | Checks whether a given string needs quoting when interpreted as an
107-
-- 'Identifier'. Simple identifiers that don't need quoting match the
108-
-- regular expression @^[a-zA-Z_][a-zA-Z0-9_'-]*$@.
116+
-- 'Identifier'.
109117
needsQuoting :: String -> Bool
110-
needsQuoting = isLeft . runParser (parseSimpleIdentifier >> eof) () ""
118+
needsQuoting s =
119+
s `elem` nixKeywords
120+
|| isLeft (runParser (parseSimpleIdentifier >> eof) () "" s)
121+
122+
-- | List of strings that are parseable as simple identifiers (see
123+
-- 'parseSimpleIdentifier') in isolation, but won't be accepted by Nix because
124+
-- [keywords](https://nix.dev/manual/nix/2.30/language/identifiers.html#keywords)
125+
-- take precedence.
126+
nixKeywords :: [String]
127+
nixKeywords =
128+
[ "assert", "with", "if", "then", "else", "let", "in", "rec", "inherit", "or" ]
111129

112130
-- | Helper function to quote a given identifier string if necessary.
113131
--

language-nix/test/hspec.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Main (main) where
22

33
import Control.Lens
4+
import Control.Monad (forM_)
45
import Data.String (fromString)
56
import Language.Nix.Identifier
67
import Test.Hspec
@@ -25,6 +26,11 @@ main = hspec $ do
2526
it "can parse the result of quote" $
2627
stringIdentProperty $ \str -> parseM "Identifier" (quote str) == Just (ident # str)
2728

29+
describe "nixKeywords" $ do
30+
it "are quoted" $ forM_ nixKeywords $ \str -> do
31+
shouldSatisfy str needsQuoting
32+
prettyShow (ident # str) `shouldBe` "\"" ++ str ++ "\""
33+
2834
stringIdentProperty :: (String -> Bool) -> Property
2935
stringIdentProperty p = property $ \s ->
3036
'\0' `notElem` s ==> classify (needsQuoting s) "need quoting" $ p s

0 commit comments

Comments
 (0)