Craft3e-0.1.0.4: Chapter19/Solutions19.hs
------------------------------------------------------------------------------
--
-- Haskell: The Craft of Functional Programming
-- Simon Thompson
-- (c) Addison-Wesley, 2011.
--
-- Solutions19
--
------------------------------------------------------------------------------
module Solutions19 where
import RegExp
import ParseLib
import Data.Char (isLower)
import Test.QuickCheck
import QC
import QCfuns
--
-- Solution 19.1
--
interp :: RE -> RegExp
interp Eps = epsilon
interp (Ch ch) = char ch
interp (e1 :|: e2) = interp e1 ||| interp e2
interp (e1 :*: e2) = interp e1 <*> interp e2
interp (St e) = star (interp e)
interp (Plus e) = i <*> star i
where
i = interp e
--
-- Solution 19.2
--
-- First pretty printing, which shows the grammar used.
-- 'e' is the syntax for epsilon, here.
prettyRE :: RE -> String
prettyRE Eps = "e"
prettyRE (Ch ch) = [ch]
prettyRE (e1 :|: e2) = "("++ prettyRE e1 ++"|"++ prettyRE e2 ++ ")"
prettyRE (e1 :*: e2) = "("++ prettyRE e1 ++ prettyRE e2 ++ ")"
prettyRE (St e) = "("++ prettyRE e ++ ")*"
prettyRE (Plus e) = "("++ prettyRE e ++ ")+"
-- Little parsers
epsP, charP :: Parse Char RE
epsP = spot (=='e') `build` const Eps
charP = spot isLowerNoE `build` Ch
isLowerNoE ch = isLower ch && ch/='e'
altP :: Parse Char RE -> Parse Char RE -> Parse Char RE
altP p1 p2
= (spot (=='(') >*>
p1 >*>
spot (=='|') >*>
p2 >*>
spot (==')'))
`build`
\ (_,(e1,(_,(e2,_)))) -> e1 :|: e2
seqP :: Parse Char RE -> Parse Char RE -> Parse Char RE
seqP p1 p2
= (spot (=='(') >*>
p1 >*>
p2 >*>
spot (==')'))
`build`
\ (_,(e1,(e2,_))) -> e1 :*: e2
starP :: Parse Char RE -> Parse Char RE
starP p
= (spot (=='(') >*>
p >*>
spot (==')') >*>
spot (=='*'))
`build`
\ (_,(e,(_,_))) -> St e
-- pulling them together
reP :: Parse Char RE
reP = epsP
`alt`
charP
`alt`
altP reP reP
`alt`
seqP reP reP
`alt`
starP reP
-- top-level function.
parseRE :: String -> RE
parseRE st
= e
where
[(e,"")] = reP st
-- Expected property: the two functions are inverses of each other, when applied to legal
-- representations of strings.
-- To test in QuickCheck, note that it's difficult to generate legal strings directly,
-- instead best to generarte REs and turn them into legal strings.
--
-- Solution 19.3
--
palin :: RE
palin = (middle :|: (a :*: (palin :*: a))) :|: (b :*: (palin :*: b))
middle = (Eps :|: (a :|: b))
--
-- Solution 19.4
--
-- Just follow the pattern of recursion used in the definition of reP above.
-- Works just like 19.3.
--
-- Solution 19.5
--
-- I believe that "recursive regular expressions" = "context free grammars" and
-- so this set of strings will therefore not be representable.
--
-- Solution 19.6
--
-- What does extension mean? Add a construct to RE and then extend its
-- interpretations into RegExp, enumeration, concrete syntax etc.
-- MatchN Int RE, interpreted by
matchN :: Int -> RegExp -> RegExp
matchN n re
| n<=0 = epsilon
| otherwise = re <*> matchN (n-1) re
--- Ranges etc. are all pretty straightforward.
--
-- Solution 19.7
--
--- Actually not so difficult to implement ...
matchBoth :: RegExp -> RegExp -> RegExp
matchBoth re1 re2 st
= re1 st && re2 st
matchNot :: RegExp -> RegExp
matchNot re st
= not (re st)
--
-- Solutions 19.8-10
--
-- See the module PositionedImages.hs
--
-- Solution 19.11
--
-- This was discussed in Solutions12, question 12.19.
--
-- Solution 19.12
--
samplePretty :: IO ()
samplePretty
= do exprs <- sample' (arbitrary :: Gen Expr)
printLines (map ((++"\n").prettyE) exprs)
printLines :: [String] -> IO ()
printLines strs
= if strs == []
then return ()
else do putStr (head strs)
printLines (tail strs)
--
-- Solution 19.13
--
-- Generators standard.
-- Properties
-- - should be able to round trip exp -> pretty -> exp
-- - not so obvious how to test the fact that the evaluator gives
-- the right result.
-- - one idea is to build pairs of expression and their values, which
-- are generated simultaneously ,,, of course, that is tantamount
-- to defining a second evaluation function (albeit implicitly).
--
-- Solution 19.14
--
-- Five finger exercise ...