From b5482d87850a53002c2ed26382068e9bba748e09 Mon Sep 17 00:00:00 2001 From: James Brechtel Date: Mon, 18 May 2026 22:58:01 -0400 Subject: [PATCH] Phase 3: End-of-line and inline block comment parsing - Add pEndComment parser for -- end-of-line comments - Add pInlineComment parser for [- ... -] block comments - Update pText to stop before -- and [- sequences using notFollowedBy - Add tests for both comment types (2 new tests, 19 total) --- src/Roux/Parser.hs | 20 ++++++++++++++++++-- test/Roux/ParserSpec.hs | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/Roux/Parser.hs b/src/Roux/Parser.hs index ac503ee..e7e2a93 100644 --- a/src/Roux/Parser.hs +++ b/src/Roux/Parser.hs @@ -111,6 +111,8 @@ pStepBlock = do pStepItem :: Parser StepItem pStepItem = pLineBreak + P.<|> P.try pInlineComment + P.<|> P.try pEndComment P.<|> P.try pRecipeRef P.<|> P.try pIngredient P.<|> P.try pCookware @@ -250,6 +252,20 @@ readMaybeDouble t = case reads (T.unpack t) of [(n, "")] -> Just n _ -> Nothing +-- | Inline block comment: [- comment text -] +pInlineComment :: Parser StepItem +pInlineComment = do + _ <- P.string "[-" + content <- P.manyTill P.anyChar (P.try (P.string "-]")) + return (StepComment (T.pack content)) + +-- | End-of-line comment: -- rest of line +pEndComment :: Parser StepItem +pEndComment = do + _ <- P.string "--" + content <- P.many (P.noneOf "\n") + return (StepEndComment (T.strip (T.pack content))) + -- | Cookware: #name or #multi word name{} pCookware :: Parser StepItem pCookware = P.char '#' *> (StepCookware . Cookware . T.pack <$> pNameWithBraces) @@ -279,10 +295,10 @@ pTimer = do Just q -> return (Duration (quantityAmount q) (quantityUnit q)) Nothing -> P.parserFail ("invalid duration: " <> content) --- | Plain text (everything up to a special character or newline). +-- | Plain text (everything up to a special character, newline, or comment start). pText :: Parser StepItem pText = do - chars <- P.many1 (P.noneOf ['\\', '\n', '@', '#', '~']) + chars <- P.many1 (P.notFollowedBy (P.string "--") *> P.notFollowedBy (P.string "[-") *> P.noneOf ['\\', '\n', '@', '#', '~']) return (StepText (T.pack chars)) -- --------------------------------------------------------------------------- diff --git a/test/Roux/ParserSpec.hs b/test/Roux/ParserSpec.hs index 1f60db0..71105f7 100644 --- a/test/Roux/ParserSpec.hs +++ b/test/Roux/ParserSpec.hs @@ -259,6 +259,40 @@ spec = do } parseCookFile input `shouldBe` expected + describe "Comments" $ do + it "parses an end-of-line comment within a step" $ do + let input = "Mash until smooth -- alternatively, boil 'em first" + case parseCookFile input of + Left err -> expectationFailure ("parse failed: " <> err) + Right recipe -> do + let body = NE.toList (sectionBody (NE.head (recipeSections recipe))) + case body of + [SecStep (Step items)] -> do + let expected = + [ StepText "Mash until smooth " + , StepEndComment "alternatively, boil 'em first" + ] + items `shouldBe` expected + other -> expectationFailure ("expected single SecStep, got " <> show other) + + it "parses an inline block comment" $ do + let input = "Slowly add @milk{4%cup} [- TODO change units to litres -], keep mixing" + case parseCookFile input of + Left err -> expectationFailure ("parse failed: " <> err) + Right recipe -> do + let body = NE.toList (sectionBody (NE.head (recipeSections recipe))) + case body of + [SecStep (Step items)] -> do + let expected = + [ StepText "Slowly add " + , StepIngredient (Ingredient "milk" (Just (Quantity 4 (Just "cup") False)) Nothing) + , StepText " " + , StepComment " TODO change units to litres " + , StepText ", keep mixing" + ] + items `shouldBe` expected + other -> expectationFailure ("expected single SecStep, got " <> show other) + describe "EasyPancakes example" $ do let pancakeInput = "-- TODO add source\n"