diff --git a/CHANGELOG.md b/CHANGELOG.md index 162b3a8e..0322c7a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ ## Unreleased +* Parsing fixes + * Quotes in inherit statements (like `inherit "or";`) are now supported. + ## 0.6.0 -- 2023-10-31 * Fix escaping of interpolations after dollar signs. diff --git a/src/Nixfmt/Parser.hs b/src/Nixfmt/Parser.hs index 0c11cfc3..bb32f1bf 100644 --- a/src/Nixfmt/Parser.hs +++ b/src/Nixfmt/Parser.hs @@ -26,7 +26,7 @@ import qualified Text.Megaparsec.Char.Lexer as L (decimal) import Nixfmt.Lexer (lexeme) import Nixfmt.Types - (Ann, Binder(..), Expression(..), File(..), Fixity(..), Leaf, Operator(..), + (Ann(..), Binder(..), Expression(..), File(..), Fixity(..), Leaf, Operator(..), ParamAttr(..), Parameter(..), Parser, Path, Selector(..), SimpleSelector(..), String, StringPart(..), Term(..), Token(..), operators, tokenText) import Nixfmt.Parser.Float (floatParse) @@ -103,6 +103,21 @@ interpolation :: Parser StringPart interpolation = Interpolation <$> symbol TInterOpen <*> expression <*> rawSymbol TInterClose +-- Interpolation, but only allowing identifiers and simple strings inside +interpolationRestricted :: Parser StringPart +interpolationRestricted = Interpolation <$> + symbol TInterOpen <*> + -- simple string without dynamic interpolations + (Term <$> String <$> do + str <- string + guard $ not $ containsInterpolation str + return str + ) <*> + rawSymbol TInterClose + where + containsInterpolation (Ann str _ _) = + any (\part -> case part of { Interpolation _ _ _ -> True; _ -> False }) $ concat str + simpleStringPart :: Parser StringPart simpleStringPart = TextPart <$> someText ( chunk "\\n" *> pure "\n" <|> @@ -214,18 +229,25 @@ parens :: Parser Term parens = Parenthesized <$> symbol TParenOpen <*> expression <*> symbol TParenClose +simpleSelector :: Parser StringPart -> Parser SimpleSelector +simpleSelector parseInterpolation = + ((IDSelector <$> identifier) <|> + (InterpolSelector <$> lexeme parseInterpolation) <|> + (StringSelector <$> lexeme simpleString)) + selector :: Maybe (Parser Leaf) -> Parser Selector selector parseDot = Selector <$> - sequence parseDot <* notFollowedBy path <*> - ((IDSelector <$> identifier) <|> - (InterpolSelector <$> lexeme interpolation) <|> - (StringSelector <$> lexeme simpleString)) <*> - optional (liftM2 (,) (reserved KOr) term) + sequence parseDot <* notFollowedBy path <*> simpleSelector interpolation selectorPath :: Parser [Selector] selectorPath = (pure <$> selector Nothing) <> many (selector $ Just $ symbol TDot) +-- Path with a leading dot +selectorPath' :: Parser [Selector] +selectorPath' = many $ try $ selector $ Just $ symbol TDot + +-- Everything but selection simpleTerm :: Parser Term simpleTerm = (String <$> string) <|> (Path <$> path) <|> (Token <$> (envPath <|> float <|> integer <|> identifier)) <|> @@ -234,9 +256,11 @@ simpleTerm = (String <$> string) <|> (Path <$> path) <|> term :: Parser Term term = label "term" $ do t <- simpleTerm - s <- many $ try $ selector $ Just $ symbol TDot - return $ case s of [] -> t - _ -> Selection t s + sel <- selectorPath' + def <- optional (liftM2 (,) (reserved KOr) term) + return $ case sel of + [] -> t + _ -> Selection t sel def -- ABSTRACTIONS @@ -271,7 +295,7 @@ abstraction = try (Abstraction <$> inherit :: Parser Binder inherit = Inherit <$> reserved KInherit <*> optional parens <*> - many identifier <*> symbol TSemicolon + many (simpleSelector interpolationRestricted) <*> symbol TSemicolon assignment :: Parser Binder assignment = Assignment <$> diff --git a/src/Nixfmt/Pretty.hs b/src/Nixfmt/Pretty.hs index eb4d7200..51a77de3 100644 --- a/src/Nixfmt/Pretty.hs +++ b/src/Nixfmt/Pretty.hs @@ -11,7 +11,7 @@ module Nixfmt.Pretty where import Prelude hiding (String) import Data.Char (isSpace) -import Data.Maybe (fromMaybe) +import Data.Maybe (fromMaybe, isNothing) import Data.Text (Text, isPrefixOf, isSuffixOf, stripPrefix) import qualified Data.Text as Text (dropEnd, empty, init, isInfixOf, last, null, strip, takeWhile) @@ -62,13 +62,9 @@ instance Pretty SimpleSelector where = prettySimpleString s <> pretty trailing <> pretty leading instance Pretty Selector where - pretty (Selector dot sel Nothing) + pretty (Selector dot sel) = pretty dot <> pretty sel - pretty (Selector dot sel (Just (kw, def))) - = pretty dot <> pretty sel - <> hardspace <> pretty kw <> hardspace <> pretty def - instance Pretty Binder where pretty (Inherit inherit Nothing ids semicolon) = base $ group (pretty inherit <> softline @@ -89,7 +85,9 @@ prettyTerm :: Term -> Doc prettyTerm (Token t) = pretty t prettyTerm (String s) = pretty s prettyTerm (Path p) = pretty p -prettyTerm (Selection term selectors) = pretty term <> hcat selectors +prettyTerm (Selection term selectors Nothing) = pretty term <> hcat selectors +prettyTerm (Selection term selectors (Just (kw, def))) = + pretty term <> hcat selectors <> hardspace <> pretty kw <> hardspace <> pretty def prettyTerm (List (Ann paropen Nothing []) [] parclose) = pretty paropen <> hardspace <> pretty parclose @@ -264,13 +262,13 @@ instance Pretty [Token] where -- STRINGS isSimpleSelector :: Selector -> Bool -isSimpleSelector (Selector _ (IDSelector _) Nothing) = True -isSimpleSelector _ = False +isSimpleSelector (Selector _ (IDSelector _)) = True +isSimpleSelector _ = False isSimple :: Expression -> Bool isSimple (Term (Token (Ann (Identifier _) Nothing []))) = True -isSimple (Term (Selection t selectors)) - = isSimple (Term t) && all isSimpleSelector selectors +isSimple (Term (Selection t selectors def)) + = isSimple (Term t) && all isSimpleSelector selectors && isNothing def isSimple _ = False hasQuotes :: [StringPart] -> Bool diff --git a/src/Nixfmt/Types.hs b/src/Nixfmt/Types.hs index 042b76c0..fd09cd21 100644 --- a/src/Nixfmt/Types.hs +++ b/src/Nixfmt/Types.hs @@ -57,11 +57,12 @@ data SimpleSelector deriving (Eq, Show) data Selector - = Selector (Maybe Leaf) SimpleSelector (Maybe (Leaf, Term)) + -- `.selector` + = Selector (Maybe Leaf) SimpleSelector deriving (Eq, Show) data Binder - = Inherit Leaf (Maybe Term) [Leaf] Leaf + = Inherit Leaf (Maybe Term) [SimpleSelector] Leaf | Assignment [Selector] Leaf Expression Leaf deriving (Eq, Show) @@ -71,7 +72,7 @@ data Term | Path Path | List Leaf [Term] Leaf | Set (Maybe Leaf) Leaf [Binder] Leaf - | Selection Term [Selector] + | Selection Term [Selector] (Maybe (Leaf, Term)) | Parenthesized Leaf Expression Leaf deriving (Eq, Show) diff --git a/test/correct/quotes-in-inherit-2.nix b/test/correct/quotes-in-inherit-2.nix new file mode 100644 index 00000000..c25efa8c --- /dev/null +++ b/test/correct/quotes-in-inherit-2.nix @@ -0,0 +1,7 @@ +let + foo = 1; + "bar" = 2; + ${"baz"} = 3; + ${"in"} = 4; + +in { inherit ${"foo"} bar "baz" "in"; } diff --git a/test/correct/quotes-in-inherit.nix b/test/correct/quotes-in-inherit.nix new file mode 100644 index 00000000..a36f90d9 --- /dev/null +++ b/test/correct/quotes-in-inherit.nix @@ -0,0 +1 @@ +{ inherit ({ "in" = 1; }) "in"; } diff --git a/test/invalid/interpolation-in-inherit-1.nix b/test/invalid/interpolation-in-inherit-1.nix new file mode 100644 index 00000000..bf47290a --- /dev/null +++ b/test/invalid/interpolation-in-inherit-1.nix @@ -0,0 +1,4 @@ +let + bar = "bar"; + +in { inherit ${bar}; } diff --git a/test/invalid/interpolation-in-inherit-2.nix b/test/invalid/interpolation-in-inherit-2.nix new file mode 100644 index 00000000..431b1d1a --- /dev/null +++ b/test/invalid/interpolation-in-inherit-2.nix @@ -0,0 +1 @@ +{ inherit ${"foo" + "bar"}; }