Bossche bollen Ingredients 500 g flour 100 g butter 3 eggs 1.5 dl water 2.5 dl whipped cream 2 spoonfuls cocoa 125 g powdered sugar salt Preparation 5 min. flour, butter, 1 dl water -> mix -> pastry pastry, eggs -> add eggs one by one -> pastry pastry -> divide into small towers -> towers 20 min. towers -> bake until brown -> towers cocoa, 0.5 dl water, powderes sugar -> mix -> cocoa mix towers, cocoa mix -> coat towers with cocoa mix -> Bossche bollenIn this assignment you will define a language for recipes, write a parser and an unparser for recipes, and define several functions that compute information from recipes. Such functions could, for example, compute the calories of one portion of a certain dish, or the total time necessary to prepare the meal, or, given the number of persons, compute the quantities of the ingredients needed. With such functions, you can already do better than the Albert Heijn recipes page: if you search there for recipes which use bread (brood in Dutch), and specify two persons first, later four persons, then you will get completely different recipes! The assignment consists of the design and implementation of several sub-tasks, distributed over the two parts of this practical:
For this, you have to follow the instructions that are detailed below. A sceleton for the program to be written is already available. You can find it on the course’s webpage. The relevant files are:
- a grammar for the concrete syntax of the input
- the abstract syntax of the input
- the parser for sentences of the input language
- functions for computations on recipes
- an unparser for the output
There will be no precise definition of the language that your program should recognize. We rather give a number of examples, in the file recipes-ex. The idea is that, given these examples, you can give the definition of the language yourself. One of the goals of this assignment is that you learn how to design a language yourself. But some tricky points about the language of recipes are discussed below.
- Recipes.hs: the skeleton file where you can modify and add code to carry out the assignments;
- recipes-ex: some sample recipes that can be used to test your programs. Note that you only have to change Recipes.hs.
- Calories.hs : the energetic value (calories) of various food products (per 100 g or dl)
- ParseLib.hs: a file containing parser combinators
- The preparation section of a recipe consists of a number of actions, one per line. Only the first action has a time associated with it. You can assume that the following actions can be performed in the same time frame. For instance, in the following preparation
the first three actions can be performed in a total of 5 minutes.5 min. flour, butter, 1 dl water -> mix -> pastry pastry, eggs -> add eggs one by one -> pastry pastry -> divide into small towers -> towers 20 min. towers -> bake until brown -> towers cocoa, 0.5 dl water, powderes sugar -> mix -> cocoa mix towers, cocoa mix -> coat towers with cocoa mix -> bossche bollen- An ingredient can consist of more than one word, such as fresh fish or red onion.
- In the example sentences there are many blank spaces. They are used, for example, to align ingredients. In you explanation of the concrete syntax you do not need to deal with whitespace, but you should take care of newlines ("\n").
Concrete Syntax
We will call the input language of our recipe page Recipe. A problem is that we do not have a precise definition of this language yet. However, we do have a number of sentences of this language, from which we can infer the grammar of the concrete syntax.
- Assignment 1: Define a grammar for the concrete syntax of the language Recipe, using EBNF notation (explained in chapter 2 of the course notes). The answer to this task should be included in the file Recipes.hs as comment. In that file, there already exists part of the grammar. You only need to fill in the gaps. Make sure that you re ect the structure of the language in your grammar: don’t make everything just a string. If there is a number, a volume, or a mass, then these differences should be recongizable from your grammar. A quantity can for example be a mass, a volume, or a number. A mass can be in kilograms, . . .
Abstract Syntax
To be able to manipulate the input for our weg pages, we have to find a suitable way to represent the input in our program. In Haskell, we can use a (data)type for this purpose. The datatype is then an abstract syntax for the recipe language.
- Assignment 2: Define a Haskell (data)type Recipe that can be used as an abstract syntax for the language Recipe. The file Recipe.hs contains already part of the abstract syntax. You should fill in the gaps.
Unparsing
Unparsing is the inverse operation of parsing. For that, a function is needed that gets an abstract syntax tree as input and transforms it into a string. We want to transform an abstract syntax tree of type Recipe into a String.
- Assignment 3: Write a function unparseRecipe :: Recipe -> String that transforms an (abstract) syntax tree into a (concrete) string. The output does not have to look precisely the same as the input (there may be differences in the amount of whitespace and the alignment), but the output should otherwise be very similar to the input. The output must be a valid sentence in the language of recipes, i.e. it must be derivable from the productions of the grammar.
Parsing
The parser has to be written using parsing combinators, which are described in Chapter 3 of the college notes. The Haskell sourcecode of the functions discussed in Chapter 3 is contained in the file ParseLib.hs.
- Assignment 4: Write a parser for the language of recipes, i. e. write a function pRecipe :: Parser Char Recipe. Please pay close attention to the positions where whitespace may occur!
- Assignment 5: Write a function parseRecipe :: String -> Recipe that computes, using the parser pRecipe, an abstract syntax tree from a string containing a recipe. You do not have to define any functions to solve the following task. Just answer the question for yourself, and add the answer as a comment to your Haskell source file.
- Assignment 6: Is parseRecipe . unparseRecipe :: Recipe -> Recipe the identity function for values of type Recipe? (This assignment does not require code. Answer the question as a comment in the program).
Functions on recipes
- Assignment 7: Write a function normalizeRecipe :: Recipe -> Recipe that expresses all masses in the recipe in terms of 100 grams (one ‘ons’ in Dutch, so 1 kilogram becomes 10 ons), and all volumes in terms of deciliter.
- Assignment 8: The file Calories.hs contains a table that lists several ingredients together with their energy value in calories (per 100 g or per dl). Use this table to compute the energy value of a dish. Assume that each recipe is intended for two persons.
- Assignment 9: Write a function timeRecipe :: Recipe -> Time that computes the total number of minutes required to prepare a dish.
- Assignment 10: Write a function shoppinglist :: Int -> Recipe -> [Ingredient] that, given the number of persons participating in the meal and a recipe (which is calculated for two persons), results in a list with the ingredients needed.