Add minimal Cooklang parser with first tests
- Create Roux.Parser with parseCookFile (splits paragraphs, treats each as a plain-text step, no metadata parsing yet) - Add emptyMetadata to Roux.Types for convenience - Add test dependencies: hspec, tasty-golden, tasty-hspec - Add ParserSpec with 4 tests covering single step, multiple steps, empty input, and whitespace trimming - Wire ParserSpec into test runner via tasty-hspec
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
{- | Parser for Cooklang (.cook) recipe files.
|
||||
|
||||
Currently a minimal placeholder that splits input into paragraphs and
|
||||
treats them as plain-text steps. Proper inline parsing (ingredients,
|
||||
cookware, timers, …) and metadata support will be added incrementally.
|
||||
-}
|
||||
module Roux.Parser
|
||||
( parseCookFile
|
||||
) where
|
||||
|
||||
import Data.List.NonEmpty (NonEmpty ((:|)))
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
|
||||
import Roux.Types
|
||||
|
||||
-- | Parse a complete @.cook@ file into a 'Recipe'.
|
||||
--
|
||||
-- Returns 'Left' with an error message on invalid input (currently never
|
||||
-- — the placeholder always succeeds).
|
||||
parseCookFile :: Text -> Either String Recipe
|
||||
parseCookFile input =
|
||||
let paragraphs = filter (not . T.null) (map T.strip (T.splitOn "\n\n" (T.strip input)))
|
||||
in case paragraphs of
|
||||
[] ->
|
||||
Right Recipe
|
||||
{ recipeMetadata = emptyMetadata
|
||||
, recipeSections = Section Nothing (Step [] :| []) :| []
|
||||
}
|
||||
(p : ps) ->
|
||||
Right Recipe
|
||||
{ recipeMetadata = emptyMetadata
|
||||
, recipeSections = Section Nothing (stepFromText p :| fmap stepFromText ps) :| []
|
||||
}
|
||||
|
||||
-- | Turn a paragraph of text into a single 'Step' containing nothing but
|
||||
-- 'StepText'. This is a placeholder until inline parsing is implemented.
|
||||
stepFromText :: Text -> Step
|
||||
stepFromText t = Step [StepText t]
|
||||
Reference in New Issue
Block a user