Recipe Description Language Reference¶
This page provides a detailed and relatively low-level description of the recipe description language accepted by Recipe Grid.
Unlike the formal definition of the recipe description language (Formal Recipe Description Language Grammar), this description is intended for human consumption and so its description differs slightly in structure and rigour in the name of easier understanding.
Note
For a high-level, tutorial introduction to the recipe description language, you may prefer the tutorial.
Note
It may be helpful to be aware of the Recipe Grid data model to understand certain details below.
Recipe Building Blocks¶
Ingredients¶
Ingredient ::= [Quantity ["of" | "of the"]] String
An ingredient is defined by an optional quantity and proposition, followed by a name.
Aside from display purposes, an ingredient’s name may be used to infer the name for a sub recipe involving only this ingredient but otherwise has no special meaning.
The optional preposition (‘of’ or ‘of the’) is allowed for readability and does not form part of the ingredient name.
When no quantity is given, the ingredient is treated as unscaled. This is useful for ingredients such as seasonings where no specific quantity is given.
Examples:
salt
2 onions
1tsp of chilli powder
References¶
Reference ::= [(Quantity | Proportion) ["of" | "of the"]] String
A reference to a sub recipe output consists of an optional quantity or proportion and preposition and then the name of the sub recipe output being referenced.
The String at the end of the reference must (case insensitively) match a previously defined sub recipe output name.
Note
There is a (potential) ambiguity between what is a reference and what is an ingredient, for example “Spam” or “100g of the spam” could both plausibly be an ingredient or a reference. This ambiguity is resolved by determining if the name provided has already been defined as a sub recipe output. If it has then the input is parsed as a reference, otherwise it is parsed as an ingredient.
When a Quantity or Proportion are not given, the reference is assumed to consume the whole of the referenced sub recipe. When a Quantity or Proportion are given, the reference consumes only the specified amount, leaving the remainder to be consumed by other references.
Note
Unitless Quantities and non-percentage Proportions present a potential ambiguity (e.g. is ‘1/2 onion’ a reference to half an onion (i.e. 1/2 is a Quantity) or half of the total quantity of onions (i.e. 1/2 is a Proportion)).
The ambiguity is resolved as follows: when a number is immediately followed by a preposition (i.e. “of” or “of the”) or by an asterisk (“*”), it is treated as a proportion. Otherwise, the number is treated as a unitless quantity.
References are substituted for the referenced sub recipes (i.e. inlined) when the reference is the only reference to that sub recipe, the sub recipe has only one output and the whole amount of that sub recipe is consumed.
Inlining is not performed when referencing a sub recipe defined within a different block in documents which divide a recipe into multiple blocks.
Examples:
spam
(the whole of sub recipe ‘spam’)2 tomatoes
(2 of the unitless sub recipe ‘tomatoes’)100g cheese
(100g of the sub recipe ‘cheese’)1/2 of the eggs
(half of the sub recipe ‘eggs’)1/2 * eggs
(half of the sub recipe ‘eggs’)50% pastry
(half of the sub recipe ‘pastry’)remaining pastry
(any remaining amount of the sub recipe ‘pastry’)
Steps¶
Step ::= String "(" Expression ("," Expression)* [","] ")"
A step consists of a String describing the step followed by a parenthesised, comma-delimited list of input Expressions (i.e. other Steps, Ingredients or References). There must always be at least one input.
Note
Steps may include line breaks between Expressions within the perenthesised list of inputs. (Normally line breaks are not allowed.)
Examples:
whip until thick (double cream)
fry (1 egg, oil)
Quantities¶
Quantity ::= ImplicitQuantity | ExplicitQuantity
ImplicitQuantity ::= Number [KnownUnit]
ExplicitQuantity ::= "{" Number [StaticString] "}"
A quantity with optional unit.
When a known unit is used, no surrounding curly braces are required. The following (case insensitive) unit names may be used (see recipe_grid.units: Unit conversions for details):
bag, bags, box, boxen, boxes, bulb, bulbs, can, cans, clove, cloves, cup, cups, g, gram, grams, kg, kilo, kilogram, kilograms, kilos, knob, knobs, l, lb, lbs, litre, mill, milliliter, milliliters, mills, ml, ounce, ounces, oz, ozs, pack, packet, packets, packs, pinch, pinches, pint, pints, pound, pounds, rasher, rashers, sachet, sachets, sack, sacks, strip, strips, table spoon, table spoons, tablespoon, tablespoons, tbsp, tbsps, tea spoon, tea spoons, teaspoon, teaspoons, tin, tins, tsp, tsps
When a custom unit is used (or just to be more explicit) the explicit quantity syntax may be used where the number and unit are surrounded by curly braces.
Note
There is a potential for ambiguity between the explicit syntax here and the
ScaledValueString syntax. The input is always parsed as an ExplicitQuantity
when it appears at the start of an Ingredient or Reference and starts with
a Number. To force a ScaledValueString in this case, you could add ""
(an empty string) before the ScaledValueString.
Examples:
10
2 tsp
{2 tsp}
{2 large sacks}
Proportions¶
Proportion ::= (Number ["*" | "%"]) | Remainder
Remainder ::= "remaining" | "remainder" | "rest" | "left over"
A relative proportion of a quantity. Either a Number, optionally followed by a
asterisk (*
) or followed by a percent symbol (%
), or a phrase meaning
‘remainder’.
When a Number is given followed by nothing or by a asterisk (*
), the
proportion is interpreted as a number in the range 0.0 (none) and 1.0 (all).
When the Number is followed by a percent symbol (%
) it is interpreted as a
percentage (i.e. 0.0 means None and 100.0 means all).
Finally, when a Remainder phrase (e.g. ‘remaining’) is given, the proportion is interpreted to mean ‘all of the substance which has not already been accounted for’.
Note
See the note about how ambiguities between Proportions and Quantities in References are resolved in the References section above.
Examples:
0.5
0.5 *
50%
remaining
Top-Level Structure¶
Expressions¶
Expression ::= Ingredient | Reference | Step | "(" LTRExpression ")"
An expression represents a sub tree within a recipe. Typically these are Ingredients, References and Steps defined using the syntax defined above.
If desired, a Left-to-Right expression (e.g. “2 onions, chopped, fried”) may be used but it must be wrapped in parentheses.
Examples:
1 can of spam
boil(egg)
(2 onions, chopped, fried)
Left-to-Right Expressions¶
LTRExpression ::= Expression ("," String)*
An Expression optionally processed by a series of comma-delimited steps. Each comma-delimited String following the Expression is turned into a step taking the item to its left as its only input.
The left-to-right expression syntax is provided to make it more natural to express cases where an ingredient has a series of steps applied to it (and only it). For example instead of writing “fry(chop(2 onions))” you can write “2 onions, chop, fry”.
Examples:
banana, peeled
2 onions, chopped, fried
fry(bacon, oil), chop
1 can of spam
(no following steps, still technically a LTR expression)
Statements¶
Statement ::= [OutputList ("=" | ":=")] LTRExpression EndOfLine
OutputList ::= String ("," String)+
A Statement defines a tree within a recipe which might (or might not) define sub recipe.
A sub recipe is defined if an OutputList is given or when the LTRExpression defines a recipe tree containing any number of Steps and a single Ingredient.
When an output list is given, a sub recipe with the named outputs is created. The choice between “:=” and “=” defines whether, if substituted for a reference (inlined), the sub recipe should be shown with a title and thick border or not (respectively).
When no output list is given and the LTRExpression contains only a single Ingredient (e.g. “1 can of spam” or “2 onions, sliced, fried”) a sub recipe with a single output with the name inferred from the Ingredient is created. In this special case, the sub recipe output name is always omitted in rendered outputs since it should be obvious from the ingredient’s name.
In all other cases, no sub recipe is defined (though the tree of Steps, Ingredients and References will still be added to the final recipe).
Examples:
1 onion, chopped, fried
(implicitly defines a sub recipe called ‘onion’)mixed herbs = mix(basil, origarno, thyme)
(defines a sub recipe called ‘mixed herbs’)sauce := boil down(tomatoes, herbs)
(defines a sub recipe called ‘sauce’ which will be outlined and labelled in the final recipe)boiled veg, veg water = drain reserving water (boil(300g veg, 1l water))
(defines a sub recipe with two outputs, ‘boiled veg’ and ‘veg water)fry(egg, oil)
(does not implicitly define a sub recipe as multiple ingredients involved)
Recipe Descriptions¶
RecipeDescription ::= Statement+
The root of the grammar, a series of statements.
Literal Values¶
Numbers¶
Number ::= Decimal | Fraction
Decimal ::= DIGITS ["." DIGITS]
Fraction ::= [DIGITS] DIGITS "/" DIGITS
Numbers may be given as integers (e.g. “123”), decimal numbers (e.g. “1.3”), two part fractions (e.g. “4/3”) or three part fractions (e.g. “1 1/3”).
Examples:
123
1.23
1/3
1 2/3
Strings¶
String ::= (StaticString | ScaledValueString)+
StaticString ::= (UnquotedString | SingleQuotedString | DoubleQuotedString)+
There are four kinds of strings matched by String:
- UnquotedString
A naked string, without any quotes around it. It may be made up of any series of characters excluding
"',:=/(){}
and starting with any non-whitespace character and ends with any non-whitespace character. There are no escape characters. UnquotedStrings are treated as plain strings.Example:
this is a string with 41 characters in it
- SingleQuotedString
A string enclosed in single quotes (
'
). May contain single-character backslash escape sequences (e.g.\'
). SingleQuotedStrings are treated as plain strings.Example:
'this is a \'SingleQuotedString\''
- DoubleQuotedString
A string enclosed in double quotes (
"
). Identical to SingleQuotedString except for the type of quotes.Example:
"this is a \"DoubleQuotedString\""
- ScaledValueString
A string enclosed in curly braces (
{
and}
). Like SingleQuotedString and DoubleQuotedString, single character backslash escape sequences are supported. Unlike all other string types, substrings containing Numbers will be scaled along with all Quantities in the recipe.Example:
{divide into 8 burgers}
In the example above, the number (8) will be scaled with the recipe. For example if the recipe is halved, it will be replaced with 4.
A String (or StaticString) may consist of any sequence of adjacent string types
which will be concatenated together in the parsed string literal, including any
whitespace between them. For example the string pack of {8} hot dog rolls
"(pre-sliced)"
will be parsed as a string literal containing pack of 8 hot
dog rolls (pre-sliced)
where the ‘8’ is a number which will be rescaled with
the recipe.
See also recipe_grid.scaled_value_string
.