pinchot-0.24.0.0: lib/Pinchot/Examples/Newman.hs
-- | This module provides a simple example of use of everything in
-- Pinchot. 'address' parses a postal address from a string and
-- prints a simple report showing some of the elements of the
-- address and their locations.
module Pinchot.Examples.Newman where
import Pinchot
import Pinchot.Pretty
import Pinchot.Examples.Earley
import Pinchot.Examples.SyntaxTrees
import Pinchot.Examples.Terminalize
import Pinchot.Examples.RulesToOptics
import qualified Control.Lens as Lens
import Data.List (intersperse)
import Data.List.NonEmpty (NonEmpty, toList)
import qualified Data.List.NonEmpty as NE
import qualified Text.Earley as Earley
import qualified Text.Show.Pretty as Pretty
-- | Formats a 'Loc' for nice on-screen display.
labelLoc :: Loc -> String
labelLoc (Loc l c p)
= "(line: " ++ show l ++ " col: " ++ show c ++ " pos: "
++ show p ++ ")"
-- | Labels a single field, where the field may or may not appear in
-- a parsed result.
labelOpt :: String -> [(Char, Loc)] -> String
labelOpt l sq
= l ++ ": " ++ show (fmap fst $ sq)
++ " " ++ loc ++ "\n"
where
loc = case Lens.uncons sq of
Nothing -> "(no location)"
Just ((_, loc), _) -> labelLoc loc
-- | Labels a single field, where the field will always appear in a
-- parsed result.
labelNE :: String -> NonEmpty (Char, Loc) -> String
labelNE l sq
= l ++ ": " ++ show (toList . fmap fst $ sq)
++ " " ++ loc ++ "\n"
where
loc = labelLoc . snd . NE.head $ sq
-- | Formats a single 'Address' for nice on-screen display.
showAddress :: Address Char Loc -> String
showAddress a = name ++ street ++ city
where
name = labelNE "Name" . t'Words
. _r'NameLine'0'Words . _r'Address'0'NameLine $ a
street = number ++ pre ++ streetName ++ suf
where
number = labelNE "Number" . t'Number . _r'StreetLine'0'Number
. _r'Address'1'StreetLine $ a
pre = labelOpt "Direction prefix"
. maybe [] NE.toList
. Lens.preview (r'Address'1'StreetLine
. r'StreetLine'2'DirectionSpace'Opt
. Lens._Wrapped'
. Lens._Just
. r'DirectionSpace'0'Direction
. Lens.to t'Direction)
$ a
streetName = labelNE "Street"
. t'StreetName
. _r'StreetLine'3'StreetName
. _r'Address'1'StreetLine
$ a
suf = labelOpt "Street suffix"
. maybe [] NE.toList
. Lens.preview (r'Address'1'StreetLine
. r'StreetLine'4'SpaceSuffix'Opt
. Lens._Wrapped'
. Lens._Just
. r'SpaceSuffix'1'Suffix
. Lens.to t'Suffix)
$ a
city = cty ++ st ++ zip
where
cty = labelNE "City"
. t'City
. _r'CityLine'0'City
. _r'Address'2'CityLine
$ a
st = labelNE "State"
. t'State
. _r'CityLine'3'State
. _r'Address'2'CityLine
$ a
zip = labelNE "Zip"
. t'ZipCode
. _r'CityLine'5'ZipCode
. _r'Address'2'CityLine
$ a
-- | Formats successful 'Address' parses and the 'Earley.Report' for
-- nice on-screen display.
showParseResult
:: ([Address Char Loc], Earley.Report String [(Char, Loc)])
-> String
showParseResult (addresses, report) = addresses' ++ "\n" ++ report'
where
addresses' = ("Full parses:\n\n" ++)
. concat . intersperse "---\n" . map showAddress
$ addresses
report' = ("Earley report:\n\n" ++) . show
$ report { Earley.unconsumed = fmap fst
. Earley.unconsumed $ report }
-- | Parse an address and print the resulting report. Good for use
-- in GHCi.
address :: String -> IO ()
address = putStrLn . showParseResult . locatedFullParses addressGrammar
-- | Like 'address' but uses 'Pretty.valToStr'.
addressPretty :: String -> IO ()
addressPretty = putStrLn . Pretty.valToStr
. prettyFullParses . locatedFullParses addressGrammar
-- | Read an address from a file and print the resulting report.
-- Good for use in GHCi.
addressFromFile
:: String
-- ^ Filename
-> IO ()
addressFromFile fn = readFile fn >>= address