Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
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).
  • Loading branch information
sternenseemann committed Sep 15, 2025
commit 641787251eed74f7a44d30a3a8b3f992a4e9e6e2
10 changes: 9 additions & 1 deletion language-nix/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Revision History for language-nix

## unreleased
## 2.3.0 (unreleased)

* `Language.Nix.Identifier` now exports `nixKeywords` which lists
keywords in Nix that are parseable as simple identifiers, but have
special meaning, like `assert` and `with`. Consequently, they can't
be used as (simple) identifiers in Nix code.

`quote`, `needsQuoting` and `Pretty` will take this list into account
and quote such identifiers. However, `HasParser` will _not_ reject them
even if they are unquoted.
* Add an hspec/QuickCheck based test suite.
2 changes: 1 addition & 1 deletion language-nix/language-nix.cabal
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: language-nix
version: 2.2.0
version: 2.3.0
synopsis: Data types and functions to represent the Nix language
description: Data types and useful functions to represent and manipulate the Nix
language.
Expand Down
26 changes: 22 additions & 4 deletions language-nix/src/Language/Nix/Identifier.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
{-# LANGUAGE TemplateHaskell #-}

module Language.Nix.Identifier
( Identifier, ident, quote, needsQuoting
( Identifier, ident, quote, needsQuoting, nixKeywords
, parseSimpleIdentifier, parseQuotedIdentifier
-- TODO: why do we expose quote?
)
where

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

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

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

-- | Checks whether a given string needs quoting when interpreted as an
-- 'Identifier'. Simple identifiers that don't need quoting match the
-- regular expression @^[a-zA-Z_][a-zA-Z0-9_'-]*$@.
-- 'Identifier'.
needsQuoting :: String -> Bool
needsQuoting = isLeft . runParser (parseSimpleIdentifier >> eof) () ""
needsQuoting s =
s `elem` nixKeywords
|| isLeft (runParser (parseSimpleIdentifier >> eof) () "" s)

-- | List of strings that are parseable as simple identifiers (see
-- 'parseSimpleIdentifier') in isolation, but won't be accepted by Nix because
-- [keywords](https://nix.dev/manual/nix/2.30/language/identifiers.html#keywords)
-- take precedence.
nixKeywords :: [String]
nixKeywords =
[ "assert", "with", "if", "then", "else", "let", "in", "rec", "inherit", "or" ]

-- | Helper function to quote a given identifier string if necessary.
--
Expand Down
6 changes: 6 additions & 0 deletions language-nix/test/hspec.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module Main (main) where

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

describe "nixKeywords" $ do
it "are quoted" $ forM_ nixKeywords $ \str -> do
shouldSatisfy str needsQuoting
prettyShow (ident # str) `shouldBe` "\"" ++ str ++ "\""

stringIdentProperty :: (String -> Bool) -> Property
stringIdentProperty p = property $ \s ->
'\0' `notElem` s ==> classify (needsQuoting s) "need quoting" $ p s
Expand Down