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)
This commit is contained in:
2026-05-18 22:58:01 -04:00
parent e63aeedc39
commit b5482d8785
2 changed files with 52 additions and 2 deletions
+18 -2
View File
@@ -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))
-- ---------------------------------------------------------------------------
+34
View File
@@ -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"