Step 6: Adding semantic functions
4.6 Step 6: Adding semantic functions
Once we have the basic parsing functions, we need to add the semantic glue: the functions that take the results of the elements in the right hand side of a production, and convert them into the result of the left hand side. The basic rule is: Let the types do the work!
First we add semantic functions to the basic parsing functions station, time, departure, arrival, and spaces. Since function identifier already returns a string, we can take the identity function id for undefined in function station. Since id <> is the identity function, it can be omitted. To obtain a value of type Time from an integer, a character, and an integer, we have to combine the two integers in a tuple. So we take the following function
\x y z -> (x,z) for undefined in time. Now, since function time returns a value of type Time, we
can take the identity function for undefined in departure and arrival, and then we replace id <> time by just time. Finally, the result of many is a string, so for undefined in spaces we can take the identity function too.
The first semantic function for the basic parser tsstring defined in Section 4.5.1 returns an abstract syntax tree of type TS2. So the first undefined in tsstring should return a tuple of a list of things of the correct type (the first component of the type TS2) and a Station. Since many returns a list of things, we can construct such a tuple by means of the function
\x y z -> (x,z) provided many returns a value of the desired type: [(Station,Time,Time)]. Note
that this semantic function basically only throws away the value returned by the spaces parser: we are not interested in the spaces between the components of our travelling scheme. The many parser returns a value of the correct type if we replace the second occurrence of undefined in tsstring by the function
\u v w x y z -> (v,x,z)
Grammar and Parser design
Again, the results of spaces are thrown away. This completes a parser for travelling schemes.
The next semantic functions we define compute the net travel time. To compute the net travel time, we have to compute the travel time of each trip from a station to a station, and to add the travel times of all of these trips. We obtain the travel time of a single trip if we replace the second occurrence of undefined by:
\u v w (xh,xm) y (zh,zm) -> (zh-xh)60 + zm-xm and Haskell’s prelude function sum sums these times, so for the first occurrence of
undefined we take:
\x y z -> sum x The final set of semantic functions we define are used for computing the total waiting
time. Since the second grammar of Section 4.1 combines arrival times and departure times, we use a parser based on this grammar: the basic parser tstoken2. We have to give definitions of the three undefined semantic functions. If a trip consists of
a single station, there is now waiting time, so the last occurrence of undefined is the function const 0. The second occurrence of function undefined computes the waiting time for one intermediate station:
\(uh,um) v (wh,wm) -> (wh-uh)60 + wm-um Finally, the first occurrence of undefined sums the list of waiting time obtained by
means of the function that replaces the second occurrence of undefined:
\s t x y z -> sum x