summaryrefslogtreecommitdiff
path: root/src/lib/Codec/Pesto/Lint.lhs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/Codec/Pesto/Lint.lhs')
-rw-r--r--src/lib/Codec/Pesto/Lint.lhs104
1 files changed, 53 insertions, 51 deletions
diff --git a/src/lib/Codec/Pesto/Lint.lhs b/src/lib/Codec/Pesto/Lint.lhs
index 58f9ab0..3ecdfa1 100644
--- a/src/lib/Codec/Pesto/Lint.lhs
+++ b/src/lib/Codec/Pesto/Lint.lhs
@@ -25,11 +25,11 @@ Linting
Not every graph generated in the previous section is a useful recipe. Some
instruction sequences just do not make sense. The tests in this section can
detect those. Failing any of them does not render a stream of instructions or
-graph invalid. They just does not describe a *useful* recipe. Thus
-implementations must not generate or export such documents. However they should
+graph invalid. They just do not describe a *useful* recipe. Thus
+implementations must not generate or export such documents. However, they should
accept input that fails any of the tests and warn the user about the failure.
-Additionally this section provides guidance on how to use the instructions
+Additionally, this section provides guidance on how to use the instructions
provided by the Pesto language properly.
Graph properties
@@ -37,9 +37,9 @@ Graph properties
.. _resultsused:
-The graph must have exactly one root node (i.e. a node with incoming edges
+The graph must have exactly one root node (i.e., a node with incoming edges
only). This also requires all results and alternatives to be referenced
-somewhere. Directives are either consumed when parsing, generating a graph and
+somewhere. Directives are either consumed when parsing, generating a graph, and
linting. Otherwise they are dangling as well. Unknown instructions are always
dangling.
@@ -54,15 +54,15 @@ Empty recipes or circular references have no root node:
> testConnectivity = [
> cmpLint "" [LintResult NoRootNode [], LintResult NoMetadata []]
> , cmpLint "*foobar >foobar"
-> [LintResult NoRootNode [], LintResult CircularLoop [0, 1], LintResult NoMetadata []]
+> [LintResult NoRootNode [], LintResult CircularLoop [0, 1], LintResult NoMetadata []]
> , cmpLint "+foobar"
-> [LintResult NonResultRootNode [0], LintResult NoMetadata []]
+> [LintResult NonResultRootNode [0], LintResult NoMetadata []]
Directives and unknown instructions are dangling and thus root nodes.
> , cmpLint "invalid %invalid +foo >bar"
> [LintResult MoreThanOneRootNode [0,1,3], LintResult NoMetadata []]
-> ]
+> ]
Metadata
++++++++
@@ -75,11 +75,11 @@ title (object) of the recipe.
> Just $ (i, ("title", MetaStr title))
> :(i, ("yield", MetaQty q))
> :foldl f [] (incomingNodes nodes edges i)
-> _ -> Nothing
+> _ -> Nothing
> where
Additional key-value metadata for the whole recipe can be added as annotations
-to the root node. If multiple annotations with the same key exist the key maps
+to the root node. If multiple annotations with the same key exist, the key maps
to a list of those values. Annotations that are unparseable key-value pairs are
added as recipe description instead.
@@ -103,8 +103,8 @@ colon char. A value may be empty.
> checkKey xs (_, (k, _)) | isKeyKnown k = xs
> checkKey xs (i, _) = LintResult UnknownMetadataKey [i]:xs
-Valid metadata keys are listed below. Additionally applications may add keys by
-prefixing them with “x-myapp-”, thus an application called “basil” adding
+Valid metadata keys are listed below. Additionally, applications may add keys by
+prefixing them with “x-myapp-”. Thus an application called “basil” adding
“some-key” would use the full key “x-basil-some-key”.
> isKeyKnown k = k `elem` knownKeys || "x-" `isPrefixOf` k
@@ -115,7 +115,7 @@ The following metadata keys are permitted:
The title, description and yield are implicit.
-> "title"
+> "title"
> , "description"
> , "yield"
@@ -137,11 +137,11 @@ An image can be a relative file reference or URI
For instance a german language recipe for one person would look like this:
> testMetadata = [
-> cmpLintMeta "+foo >1 ml foobar (language: de) (x-app-key: value)"
-> []
-> (Just [(1, ("title", MetaStr "foobar"))
-> , (1, ("yield", MetaQty (Quantity (Exact (AmountRatio (1%1))) "ml" "foobar")))
-> , (2, ("language", MetaStr "de"))
+> cmpLintMeta "+foo >1 _ foobar (language: de) (x-app-key: value)"
+> []
+> (Just [(1, ("title", MetaStr "foobar"))
+> , (1, ("yield", MetaQty (Quantity (Exact (AmountRatio (1%1))) "" "foobar")))
+> , (2, ("language", MetaStr "de"))
> , (3, ("x-app-key", MetaStr "value"))])
Unparseable annotations or unknown keys are linting errors:
@@ -149,7 +149,7 @@ Unparseable annotations or unknown keys are linting errors:
> , cmpLintMeta "+foo >foobar (unknown-key: value)"
> [LintResult UnknownMetadataKey [2]]
> (Just [(1, ("title", MetaStr "foobar"))
-> , (1, ("yield", MetaQty (strQuantity "foobar")))
+> , (1, ("yield", MetaQty (strQuantity "foobar")))
> , (2, ("unknown-key", MetaStr "value"))])
Root node annotations not containing a parseable key-value pair are assigned
@@ -169,7 +169,7 @@ the key “description”.
Time is a tool
++++++++++++++
-By definition time is a tool and not an ingredient.
+By definition, time is a tool and not an ingredient.
> timeUnits = ["s", "min", "h", "d"]
>
@@ -179,7 +179,7 @@ By definition time is a tool and not an ingredient.
> timeIsATool nodes _ = foldl f [] nodes
> where
> f xs (nodeid, Ingredient q) | isTime q = LintResult TimeIsATool [nodeid]:xs
-> f xs _ = xs
+> f xs _ = xs
> testLintQuantity = [
> cmpLint "+10 min >foo" [LintResult TimeIsATool [0]]
@@ -189,9 +189,9 @@ By definition time is a tool and not an ingredient.
> , cmpLint "&10 min [bar] >foo" []
> ]
-Only actions can be annotated with a time. It can be used to indicate how long
-a certain action is *expected* to take (i.e. peeling potatoes takes two
-minutes) or how long the action is supposed to be executed (i.e. cook five
+Only actions can be annotated like this. It can be used to indicate how long
+a particular action is *expected* to take (i.e., peeling potatoes takes two
+minutes) or how long the action is supposed to be executed (i.e. cook for five
minutes). More time annotations improve the software’s scheduling capabilities.
> timeAnnotatesAction nodes edges = foldl f [] nodes
@@ -201,7 +201,7 @@ minutes). More time annotations improve the software’s scheduling capabilities
> toNodelist = (!!) nodes . snd
> allActions = all (isAction . snd . toNodelist)
-For example “cook 10 minutes” can be expressed with
+For example, “cook 10 minutes” can be expressed with:
> testTimeAnnotatesAction = [
> cmpLint "&10 min [cook] >soup" []
@@ -216,10 +216,10 @@ For example “cook 10 minutes” can be expressed with
Well-known units
++++++++++++++++
-Units can be an arbitrary strings, but implementations should recognize the
-common metric units g (gram), l (litre) and m (metre). One of these prefixes
-may be used with each of them: m (milli-), c (centi-), d (dezi-) and k (kilo-).
-Additionally time in s (second), min (minute), h (hour), d (day) should be
+Units can be arbitrary strings, but implementations should recognize the
+standard metric units g (gram), l (liter), and m (meter). One of these prefixes
+may be used with each of them: m (milli-), c (centi-), d (dezi-), and k (kilo-).
+Additionally, time in s (second), min (minute), h (hour), and d (day) should be
accepted.
> wellKnownUnit nodes _ = foldl f [] nodes
@@ -232,7 +232,7 @@ accepted.
> extractQty _ = Nothing
> f xs (nodeid, instr) | fromMaybe False (extractQty instr >>= (return . not . known)) =
> LintResult UnitNotWellKnown [nodeid]:xs
-> f xs _ = xs
+> f xs _ = xs
> known (Quantity _ unit _) = unit `elem` knownUnits
> knownUnits = [
> ""
@@ -241,9 +241,9 @@ accepted.
> , "cm", "dm", "m"
> ] ++ timeUnits
-Usage of imperial units (inch, pound, …) as well as non-standard
-units like “teaspoon”, “cup” or similar is discouraged because
-the former is used by just three countries in the world right now and
+Usage of imperial units (inch, pound, …), non-standard
+units like “teaspoon,” “cup,” or similar is discouraged because
+the former is used by just three countries in the world right now, and
the latter is language- and country-dependent. The implementation may
provide the user with a conversion utility.
@@ -268,14 +268,14 @@ The unit is case-sensitive, thus
References
++++++++++
-All references must be resolved. An `earlier check <#resultsused>`_ makes sure
+All references must be resolved. An `earlier check <#resultsused>`_ ensures
all results and alternatives are referenced at some point.
> referencesResolved nodes edges = foldl f [] nodes
> where
> f xs (nodeid, Reference _) | null (incomingEdges edges nodeid) =
> LintResult UndefinedReference [nodeid]:xs
-> f xs _ = xs
+> f xs _ = xs
> testLintRefs = [
> cmpLint "*foobar >foobar >barbaz" [LintResult CircularLoop [0, 1]]
@@ -283,8 +283,8 @@ all results and alternatives are referenced at some point.
> ]
Results and alternatives must not have duplicate names, so collect
-their lower-case object names into map and flag those, which reference
-multiple nodes.
+their lower-case object names into a ``Map`` and flag those which
+reference multiple nodes.
> uniqueNames nodes _ = M.foldl f [] nameMap
> where
@@ -312,7 +312,7 @@ only occur at the beginning of a recipe.
> where
> f xs (nodeid, Result _) | null (incomingEdges edges nodeid) =
> LintResult TooFewChildren [nodeid]:xs
-> f xs _ = xs
+> f xs _ = xs
> testLintResultNonempty = [
> cmpLint ">bar *bar >baz" [LintResult TooFewChildren [0]]
@@ -325,9 +325,9 @@ make the alternative pointless.
> twoAlternatives nodes edges = foldl f [] nodes
> where
-> f xs (nodeid, Alternative _) | length (incomingEdges edges nodeid) < 2 =
+> f xs (nodeid, Alternative _) | length (incomingEdges edges nodeid) < 2 =
> LintResult TooFewChildren [nodeid]:xs
-> f xs _ = xs
+> f xs _ = xs
> testLintTwoAlternatives = [
> cmpLint "+A |foo *foo >bar" [LintResult TooFewChildren [1]]
@@ -335,9 +335,11 @@ make the alternative pointless.
> , cmpLint "+A &B |foo *foo >bar" []
> ]
-References cannot loop, because, well, you cannot cook something and
+.. _reject-loops:
+
+References cannot loop because, well, you cannot cook something and
use an ingredient you have not made yet. It is possible to branch out
-and merge again though if an ingredient is split into multiple parts
+and merge again if an ingredient is split into multiple parts
and added to different outputs.
> circularLoops nodes edges = map (LintResult CircularLoop) circles
@@ -376,11 +378,11 @@ This limitation is not enforced for ranges containing strings.
> rangeFromLargerThanTo nodes _ = foldl f [] nodes
> where
-> f xs (nodeid, Ingredient q) | not $ rangeOk q =
+> f xs (nodeid, Ingredient q) | not $ rangeOk q =
> LintResult RangeFromLargerThanTo [nodeid]:xs
-> f xs (nodeid, Reference q) | not $ rangeOk q =
+> f xs (nodeid, Reference q) | not $ rangeOk q =
> LintResult RangeFromLargerThanTo [nodeid]:xs
-> f xs _ = xs
+> f xs _ = xs
> rangeOk (Quantity (Range (AmountRatio a) (AmountRatio b)) _ _) = a < b
> rangeOk _ = True
@@ -404,9 +406,9 @@ Appendix
> | DuplicateReferenceName
> | CircularLoop
> | TooFewChildren
-> | TimeIsATool
+> | TimeIsATool
> | TimeAnnotatesAction
-> | UnitNotWellKnown
+> | UnitNotWellKnown
> | InvalidNode
> | RangeFromLargerThanTo
> | NoMetadata
@@ -419,11 +421,11 @@ Every lint test checks a single aspect of the graph.
> lintTests = [
> rootIsResult
-> , referencesResolved
+> , referencesResolved
> , uniqueNames
> , circularLoops
-> , resultNonempty
-> , twoAlternatives
+> , resultNonempty
+> , twoAlternatives
> , timeIsATool
> , timeAnnotatesAction
> , wellKnownUnit
@@ -432,7 +434,7 @@ Every lint test checks a single aspect of the graph.
> ]
> withGraph doc f = f nodes edges
-> where
+> where
> (Right op) = (head . extract . snd . unzip) <$> parse ("%pesto " ++ doc)
> nodes = zip [firstNodeId..] op
> edges = toGraph nodes ++ resolveReferences nodes