|
| 1 | +{-| |
| 2 | +Module : Parsing |
| 3 | +Description : Simple Monadic Parsing Library |
| 4 | +Maintainer : Thomas Hallgren |
| 5 | +A Simple Monadic Parsing Library |
| 6 | +Used in the course Functional Programming GU/Chalmers. |
| 7 | +Original author: David Sands. |
| 8 | +
|
| 9 | +--------------------- |
| 10 | +Aim: reusable Parser combinators including |
| 11 | + a new type for the Parser, |
| 12 | + but no export of the constructor |
| 13 | +Changes (v3 2016) Thomas Hallgren |
| 14 | +Export <* and *> from class Applicative instead of the combinators <-< and >-> |
| 15 | +Export <|> from class Alternative instead of +++ |
| 16 | +Changes (v2 2015) |
| 17 | +For compatibility with GHC>=7.10 Parser |
| 18 | +is now also instance Applicative |
| 19 | +Removal: Class Functor, Applicative and Monad provide a number of |
| 20 | +functions that were previously exported explicitly, in particular |
| 21 | +(>*>) is available as the bind operation (>>=), |
| 22 | +success is return, pmap is fmap. |
| 23 | +Additional function: |
| 24 | +readsP :: Read a => Parser a |
| 25 | +-- satisfies |
| 26 | +-- parse readsP s == listToMaybe (reads s) |
| 27 | +--------------------- |
| 28 | +-} |
| 29 | +module Parsing |
| 30 | + ( -- * The Parser type |
| 31 | + Parser -- exports the type name but not the constructors |
| 32 | + ,parse |
| 33 | + -- * Basic parsers |
| 34 | + ,sat,item,digit |
| 35 | + ,readsP -- behaves as reads :: Read a => [(a,String)] |
| 36 | + , char, failure |
| 37 | + -- * Combining parsers |
| 38 | + ,oneOrMore,zeroOrMore,chain,(<:>) |
| 39 | + -- * More general operators useful in parser construction |
| 40 | + -- ** Try two alternatives |
| 41 | + ,(<|>) |
| 42 | + -- ** Apply a function to the result of a parser |
| 43 | + ,(<$>),(<*>) |
| 44 | + -- ** Parse two things, return only the first |
| 45 | + ,(<*) |
| 46 | + -- ** Parse two things, return only the second |
| 47 | + ,(*>) |
| 48 | + -- ** Return a result without consuming any input |
| 49 | + , return |
| 50 | + ) |
| 51 | +where |
| 52 | +import Data.Char |
| 53 | +import Data.Maybe(listToMaybe) |
| 54 | +-- boilerplate for GHC 10.7 compatibility: |
| 55 | +import Control.Applicative (Applicative(..),Alternative(..)) |
| 56 | +import Control.Monad (liftM, ap) |
| 57 | +------------------ |
| 58 | + |
| 59 | +-- | The abstract data type representing a Parser |
| 60 | +newtype Parser a = P (String -> Maybe (a,String)) |
| 61 | +-- | Runs the parser on the given string |
| 62 | +-- to return maybe a thing and a string |
| 63 | +parse :: Parser a -> String -> Maybe(a,String) |
| 64 | +parse (P f ) s = f s |
| 65 | +-- | A parser for anything in the Read class, |
| 66 | +-- satisfying |
| 67 | +-- |
| 68 | +-- prop> parse readsP s == listToMaybe (reads s) |
| 69 | +readsP :: Read a => Parser a |
| 70 | +readsP = P $ listToMaybe . reads |
| 71 | +------------------- |
| 72 | +-- | Parser than can never succeed |
| 73 | +failure :: Parser a -- always fails |
| 74 | +failure = P $ \s -> |
| 75 | + Nothing |
| 76 | +-- | Parser that succeeds without looking at the String |
| 77 | +success :: a -> Parser a |
| 78 | +success a = P $ \s -> |
| 79 | + Just (a,s) |
| 80 | +-- | Parse any single character |
| 81 | +item = P $ \s -> |
| 82 | + case s of |
| 83 | + (c:s') -> Just (c,s') |
| 84 | + "" -> Nothing |
| 85 | +infixr 3 +++ |
| 86 | +-- | Try the first parser and if it fails try the second |
| 87 | +(+++) :: Parser a -> Parser a -> Parser a |
| 88 | +p +++ q = P $ \s -> |
| 89 | + case parse p s of |
| 90 | + Nothing -> parse q s |
| 91 | + r -> r |
| 92 | +-- (p >*> f) parse using p to produce a. |
| 93 | +-- Then parse using f a |
| 94 | +infixl 1 >*> |
| 95 | +(>*>) :: Parser a -> (a -> Parser b) -> Parser b |
| 96 | +p >*> f = P $ \s -> |
| 97 | + case parse p s of |
| 98 | + Just (a,s') -> parse (f a) s' |
| 99 | + Nothing -> Nothing |
| 100 | +----------------------------------------------- |
| 101 | +-- Parsers below do not depend on the internal |
| 102 | +-- representation of Parser |
| 103 | +-- | parse a single character satisfying property p |
| 104 | +sat :: (Char -> Bool) -> Parser Char |
| 105 | +sat p = item >*> \a -> if p a then success a else failure |
| 106 | +-- | parse a digit character |
| 107 | +digit :: Parser Char |
| 108 | +digit = sat isDigit |
| 109 | +-- | Parse a specific character |
| 110 | +char c = sat (==c) |
| 111 | +-- example: parse any lowercase letter |
| 112 | +-- followed by its uppercase equivalent aA or bB etc. |
| 113 | +ex1 = sat isAsciiLower >*> char . toUpper |
| 114 | +-- pmap modifies the result of a parser |
| 115 | +-- | Parse a thing, then parse a list of things, and |
| 116 | +-- return the first thing followed by the list of things |
| 117 | +(<:>):: Parser a -> Parser [a] -> Parser [a] |
| 118 | +p <:> q = p >*> \a -> fmap (a:) q |
| 119 | +-- | Parse zero or more things |
| 120 | +zeroOrMore :: Parser a -> Parser [a] |
| 121 | +zeroOrMore p = oneOrMore p +++ success [] |
| 122 | +-- | Parse one or more things |
| 123 | +oneOrMore :: Parser a -> Parser [a] |
| 124 | +oneOrMore p = p <:> zeroOrMore p |
| 125 | +-- | Parse a list of as, separated by bs |
| 126 | +chain :: Parser a -> Parser b -> Parser [a] |
| 127 | +chain p q = p <:> zeroOrMore (q *> p) |
| 128 | +-- example: comma separated digits "1,2,3" |
| 129 | +-- Standard definition for Functor and Applicative for |
| 130 | +-- GHC>=7.10 compatibility |
| 131 | +instance Functor Parser where |
| 132 | + fmap = liftM |
| 133 | +instance Applicative Parser where |
| 134 | + pure = success |
| 135 | + (<*>) = ap |
| 136 | +instance Monad Parser where |
| 137 | + (>>=) = (>*>) |
| 138 | + return = pure |
| 139 | +instance Alternative Parser where |
| 140 | + empty = failure |
| 141 | + (<|>) = (+++) |
0 commit comments