{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}

-- | This module contains a context-free grammar for U.S. postal
-- addresses.  It would never hold up to real-world use but it gives
-- you a good idea of how to write grammars in Pinchot.
--
-- The grammar is ambiguous.
--
-- There are no signatures; the type of every declaration in the
-- module is 'Rule' 'Char'.
module Pinchot.Examples.Postal where

import Pinchot

rDigit = terminal "Digit" [|| \x -> x >= '0' && x <= '9' ||]
  <?> "digit from 0 to 9"

rDigits = plus rDigit

rLetter = terminal "Letter" [|| \x -> (x >= 'a' && x <= 'z')
                                   || (x >= 'A' && x <= 'Z') ||]
  <?> "letter from A to Z"

rNorth = terminal "North" [|| (== 'N') ||]

rSouth = terminal "South" [|| (== 'S') ||]

rEast = terminal "East" [|| (== 'E') ||]

rWest = terminal "West" [|| (== 'W') ||]

rNE = series "NE" "NE"

rNW = series "NW" "NW"

rSW = series "SW" "SW"

rSE = series "SE" "SE"

rDirection = union "Direction"
  [rNorth, rSouth, rEast, rWest, rNE, rNW, rSE, rSW]

rStreet = series "Street" "St"

rAvenue = series "Avenue" "Ave"

rWay = series "Way" "Way"

rBoulevard = series "Boulevard" "Blvd"

rSuffix = union "Suffix" [rStreet, rAvenue, rWay, rBoulevard]

rSpace = terminal "Space" [|| (== ' ') ||]

rComma = terminal "Comma" [|| (== ',') ||]

rNewline = terminal "Newline" [|| (== '\n') ||]

rCommaSpace = record "CommaSpace" [rComma, rSpace]

rSeparator = union "Separator" [rCommaSpace, rNewline]

rLetters = nonTerminal "Letters"
  [ ("NoLetter", [])
  , ("ConsLetter", [rLetter, rLetters])
  ]

-- Named "PostalWord" to avoid clash with Prelude.Word
rPostalWord = record "PostalWord" [rLetter, rLetters]

rPreSpacedWord = record "PreSpacedWord" [rSpace, rPostalWord]

rPreSpacedWords = star rPreSpacedWord

rWords = record "Words" [rPostalWord, rPreSpacedWords]

rNumber = wrap "Number" rDigits

rStreetName = wrap "StreetName" rWords

rCity = wrap "City" rWords

rState = wrap "State" rWords

rZipCode = record "ZipCode" [rDigit, rDigit, rDigit, rDigit, rDigit]

rDirectionSpace = record "DirectionSpace" [rDirection, rSpace]

rSpaceSuffix = record "SpaceSuffix" [rSpace, rSuffix]

rOptDirection = opt rDirectionSpace

rOptSuffix = opt rSpaceSuffix

rOptNewline = opt rNewline

rNameLine = record "NameLine" [rWords, rSeparator]

rStreetLine = record "StreetLine" [rNumber, rSpace, rOptDirection,
  rStreetName, rOptSuffix, rSeparator]

rCityLine = record "CityLine" [rCity, rComma, rSpace, rState,
  rSpace, rZipCode, rOptNewline]

rAddress = record "Address" [rNameLine, rStreetLine, rCityLine]