Phase 4: Recipe references + all 4 example file validation

- Fix pRecipeRef to include ./ prefix in stored path
- Add recipe reference test
- Add parse-validation tests for all 4 example .cook files
  (EasyPancakes, FriedRice, CoffeeSouffle, OlivierSalad)
- All 24 tests passing, all examples parse without error
This commit is contained in:
2026-05-18 22:59:44 -04:00
parent b5482d8785
commit c68d80e9c1
2 changed files with 44 additions and 1 deletions
+2 -1
View File
@@ -131,11 +131,12 @@ pLineBreak = do
pRecipeRef :: Parser StepItem
pRecipeRef = do
_ <- P.char '@'
-- Consume @ and store ./ as part of the path
_ <- P.char '.'
_ <- P.char '/'
path <- P.many (P.noneOf "{(\n\\")
mqty <- P.optionMaybe (P.between (P.char '{') (P.char '}') pQuantityBody)
return (StepRecipeRef (RecipeRef path (join mqty)))
return (StepRecipeRef (RecipeRef ("./" <> path) (join mqty)))
-- | Ingredient: @name{quantity%unit}(preparation)
pIngredient :: Parser StepItem
+42
View File
@@ -2,6 +2,7 @@ module Roux.ParserSpec (spec) where
import Data.List.NonEmpty (NonEmpty ((:|)))
import qualified Data.List.NonEmpty as NE
import qualified Data.Text.IO as TIO
import Test.Hspec (Spec, describe, expectationFailure, it, shouldBe)
import Roux.Parser (parseCookFile)
@@ -369,3 +370,44 @@ spec = do
_ ->
expectationFailure
("expected SecStep as third body item, got body=" <> show body)
describe "Recipe references" $ do
it "parses a recipe reference" $ do
let input = "Pour over with @./sauces/Hollandaise{150%g}."
case parseCookFile input of
Left err -> expectationFailure ("parse failed: " <> err)
Right recipe -> do
let items = stepItems recipe
let expected =
[ StepText "Pour over with "
, StepRecipeRef (RecipeRef "./sauces/Hollandaise" (Just (Quantity 150 (Just "g") False)))
, StepText "."
]
items `shouldBe` expected
describe "Full recipe files" $ do
let readExample name = do
content <- TIO.readFile ("cooklang-examples/" <> name <> ".cook")
case parseCookFile content of
Left err -> expectationFailure (name <> ": " <> err)
Right _ -> pure ()
it "EasyPancakes parses without error" $ do
readExample "EasyPancakes"
it "FriedRice parses without error" $ do
readExample "FriedRice"
it "CoffeeSouffle parses without error" $ do
readExample "CoffeeSouffle"
it "OlivierSalad parses without error" $ do
readExample "OlivierSalad"
-- | Extract step items from the first step of an unnamed section.
stepItems :: Recipe -> [StepItem]
stepItems r =
let s = NE.head (recipeSections r)
in case NE.head (sectionBody s) of
SecStep step -> unStep step
_ -> []