summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.rst2
-rw-r--r--src/exe/Main.lhs8
-rw-r--r--src/lib/Codec/Pesto.lhs4
-rw-r--r--src/lib/Codec/Pesto/Graph.lhs100
-rw-r--r--src/lib/Codec/Pesto/Lint.lhs9
5 files changed, 108 insertions, 15 deletions
diff --git a/README.rst b/README.rst
index 5a16132..3c027f8 100644
--- a/README.rst
+++ b/README.rst
@@ -8,5 +8,5 @@ For more information see the latest `rendered version`_ or its Literate
Haskell sources_.
.. _rendered version: https://6xq.net/pesto/
-.. _sources: https://codeberg.org/ldb/pesto/src/branch/master/src/lib/Codec/Pesto.lhs
+.. _sources: https://codeberg.org/purplesym/pesto/src/branch/master/src/lib/Codec/Pesto.lhs
diff --git a/src/exe/Main.lhs b/src/exe/Main.lhs
index dc360fd..ae663ad 100644
--- a/src/exe/Main.lhs
+++ b/src/exe/Main.lhs
@@ -8,7 +8,7 @@ User interface
> import Data.List (intercalate)
>
> import Codec.Pesto.Parse (parse, Instruction (Ingredient), Quantity (..))
-> import Codec.Pesto.Graph (extract, toGraph, firstNodeId, resolveReferences)
+> import Codec.Pesto.Graph (extract, toGraph, firstNodeId, resolveReferences, extractIngredients, mergeQuantity)
> import Codec.Pesto.Lint (lint, extractMetadata, Metadata(..), LintResult (LintResult))
> import Codec.Pesto.Serialize (serialize)
@@ -87,10 +87,10 @@ ingredients
Extract ingredients and print them in CSV format. This does not take
alternatives into account yet.
-> runIngredients stream = mapM_ (putStrLn . csvQty) $ reverse $ foldl getIngredient [] stream
+> runIngredients stream = mapM_ (putStrLn . csvQty) $ ingredients
> where
-> getIngredient xs (_, Ingredient q) = q:xs
-> getIngredient xs _ = xs
+> (nodes, _) = streamToGraph stream
+> ingredients = mergeQuantity $ extractIngredients nodes
> printMeta (_, (key, MetaStr value)) = putStrLn $ key ++ "=" ++ value
> printMeta (_, (key, MetaQty q)) = putStrLn $ key ++ "=" ++ csvQty q
diff --git a/src/lib/Codec/Pesto.lhs b/src/lib/Codec/Pesto.lhs
index ba8e332..13953f9 100644
--- a/src/lib/Codec/Pesto.lhs
+++ b/src/lib/Codec/Pesto.lhs
@@ -46,7 +46,7 @@ interpreted as described in `RFC 2119`_.
:Version: 1-draft
:License: CC0_
:Website: https://6xq.net/pesto/
-:Source code: https://codeberg.org/ldb/pesto
+:Source code: https://codeberg.org/purplesym/pesto
.. _CC0: https://creativecommons.org/publicdomain/zero/1.0/
@@ -263,7 +263,7 @@ There’s more syntax available to express alternatives (either penne or
tagliatelle), ranges (1–2 l water or approximately 1 liter water), and metadata.
But now you can have a first peek at `my recipe collection`_.
-.. _my recipe collection: https://codeberg.org/ldb/rezepte
+.. _my recipe collection: https://codeberg.org/purplesym/rezepte
.. include:: Pesto/Parse.lhs
.. include:: Pesto/Graph.lhs
diff --git a/src/lib/Codec/Pesto/Graph.lhs b/src/lib/Codec/Pesto/Graph.lhs
index 43142b6..b959d59 100644
--- a/src/lib/Codec/Pesto/Graph.lhs
+++ b/src/lib/Codec/Pesto/Graph.lhs
@@ -16,6 +16,8 @@ Language semantics
> , resolveReferences
> , test
> , extract
+> , extractIngredients
+> , mergeQuantity
> , NodeId
> , Node
> , Nodes
@@ -23,8 +25,10 @@ Language semantics
> , Edges
> ) where
> import Data.Char (toLower)
-> import Data.List (sort, nub)
+> import Data.List (sort, nub, sortOn)
> import Test.HUnit hiding (test, Node)
+> import Data.Ratio ((%), Ratio)
+> import Data.Either (fromRight)
>
> import Codec.Pesto.Parse hiding (test)
@@ -155,7 +159,9 @@ These are helper functions:
> consumeStack (_, s, edges) i =
> let
> stack = dropWhile null s
-> (top:sx) = if null stack then [[]] else stack
+> alwaysStack = if null stack then [[]] else stack
+> top = head alwaysStack
+> sx = tail alwaysStack
> in (Just i, []:top:sx, edgesTo i top ++ edges)
> edgesTo i = map (\x -> (x, i))
@@ -264,13 +270,83 @@ results with the same name). They are permitted at this stage, but rejected
> , cmpGraphRef "|foobar *foobar >foobar *foobar" [(0, 1), (0, 3), (2, 1), (2, 3)]
> ]
+Quantities
+++++++++++
+
+.. Does not really fit here. Move?
+
+Lists of quantities, for instance for a shopping basket, can be extracted and merged.
+
+> extractIngredients :: [Node Instruction] -> [Quantity]
+> extractIngredients nodes = foldl f [] nodes
+> where
+> f xs (_, Ingredient q) = q:xs
+> f xs _ = xs
+
+> mergeQuantity :: [Quantity] -> [Quantity]
+> mergeQuantity ingredients = reverse $ foldl foldQty [] sortedIngredients
+> where
+> sortedIngredients = sortOn sortKey ingredients
+> sortKey (Quantity _ b c) = (c, b)
+> foldQty ((Quantity (Exact (AmountRatio prevAmount)) prevUnit prevObject):xs)
+> (Quantity (Exact (AmountRatio qamount)) qunit qobject)
+> | prevObject == qobject && prevUnit == qunit
+> = (Quantity (Exact (AmountRatio (prevAmount + qamount))) qunit qobject):xs
+> foldQty ((Quantity (Exact (AmountStr "")) prevUnit prevObject):xs)
+> (Quantity (Exact (AmountStr "")) qunit qobject)
+> | prevObject == qobject && prevUnit == qunit
+> = (Quantity (Exact (AmountStr "")) qunit qobject):xs
+> foldQty xs x = x:xs
+
+> testQuantity :: [Test]
+> testQuantity = [
+
+Quantities are merged when their objects and units match.
+
+> cmpMerge [
+> exactRatioQuantity (1%1) "kg" "apples"
+> , exactRatioQuantity (2%1) "kg" "apples"
+> , exactRatioQuantity (1%2) "" "bananas"
+> , exactRatioQuantity (3%4) "" "bananas"
+> , exactRatioQuantity (5%1) "" "peas"
+> , exactRatioQuantity (200%1) "g" "peas"
+> ]
+> [
+> exactRatioQuantity (3%1) "kg" "apples"
+> , exactRatioQuantity (5%4) "" "bananas"
+> , exactRatioQuantity (5%1) "" "peas"
+> , exactRatioQuantity (200%1) "g" "peas"
+> ]
+
+Objects without any quantity can also be merged.
+
+> , cmpMerge [
+> strQuantity "apples"
+> , strQuantity "apples"
+> ]
+> [ strQuantity "apples" ]
+
+The merged quantities are sorted.
+
+> , cmpMerge [
+> strQuantity "b"
+> , strQuantity "c"
+> , strQuantity "a"
+> ]
+> [
+> strQuantity "a"
+> , strQuantity "b"
+> , strQuantity "c"
+> ]
+> ]
+
Appendix
++++++++
> runGraphWith f doc expect = sort edges ~?= sort expect
> where
-> (Right op) = (head . extract . snd . unzip) <$> parse ("%pesto " ++ doc)
-> nodes = zip [firstNodeId..] op
+> op = (head . extract . snd . unzip) <$> parse ("%pesto " ++ doc)
+> nodes = zip [firstNodeId..] (fromRight (error "unreachable") op)
> edges = f nodes
> cmpGraph = runGraphWith toGraph
> cmpGraphRef = runGraphWith resolveReferences
@@ -290,12 +366,26 @@ Find graph’s root node(s), that is a node without outgoing edges:
Get all nodes with edges pointing towards nodeid
> incomingEdges edges nodeid = filter ((==) nodeid . snd) edges
+
+> incomingNodes :: Nodes a -> Edges -> NodeId -> Nodes a
> incomingNodes nodes edges nodeid = map ((!!) nodes . fst) $ incomingEdges edges nodeid
> outgoingEdges edges nodeid = filter ((==) nodeid . fst) edges
> outgoingNodes nodes edges nodeid = map ((!!) nodes . snd) $ outgoingEdges edges nodeid
-> test = ["graph" ~: testGraph, "ref" ~: testRef, "extract" ~: testExtract]
+> test = [
+> "graph" ~: testGraph
+> , "ref" ~: testRef
+> , "extract" ~: testExtract
+> , "quantity" ~: testQuantity
+> ]
+
+> cmpMerge value expected = (mergeQuantity value) ~?= expected
+> strQuantity = Quantity (Exact (AmountStr "")) ""
+> exactRatioQuantity :: Ratio Integer -> Unit -> Object -> Quantity
+> exactRatioQuantity qamount qunit qobject =
+> Quantity (Exact (AmountRatio qamount)) qunit qobject
+
> third (_, _, x) = x
diff --git a/src/lib/Codec/Pesto/Lint.lhs b/src/lib/Codec/Pesto/Lint.lhs
index edeede7..5d9be7d 100644
--- a/src/lib/Codec/Pesto/Lint.lhs
+++ b/src/lib/Codec/Pesto/Lint.lhs
@@ -10,13 +10,15 @@ Linting
> , parseMetadata
> , extractMetadata
> , Metadata(..)
-> , LintResult(..)) where
+> , LintResult(..)
+> , LintStatus(..)) where
> import Test.HUnit hiding (test, Node)
> import Data.List (sort, isPrefixOf, insert, intersect)
> import Text.Parsec hiding (parse)
> import Data.Char (isSpace, toLower)
> import Data.Ratio ((%))
> import Data.Maybe (fromMaybe)
+> import Data.Either (fromRight)
> import qualified Data.Map.Strict as M
>
> import Codec.Pesto.Graph hiding (test)
@@ -70,6 +72,7 @@ Metadata
The graph’s root node must be a result. It contains yield (amount and unit) and
title (object) of the recipe.
+> extractMetadata :: Nodes Instruction -> Edges -> Maybe [(NodeId, (String, Metadata))]
> extractMetadata nodes edges = case walkRoot nodes edges of
> [(i, Result q@(Quantity _ _ title))] ->
> Just $ (i, ("title", MetaStr title))
@@ -445,8 +448,8 @@ Every lint test checks a single aspect of the graph.
> withGraph doc f = f nodes edges
> where
-> (Right op) = (head . extract . snd . unzip) <$> parse ("%pesto " ++ doc)
-> nodes = zip [firstNodeId..] op
+> op = (head . extract . snd . unzip) <$> parse ("%pesto " ++ doc)
+> nodes = zip [firstNodeId..] (fromRight (error "unreachable") op)
> edges = toGraph nodes ++ resolveReferences nodes
> cmpLint doc expect = withGraph doc (\nodes edges -> doc ~: sort (lint nodes edges) ~?= sort expect)