[Haskell-cafe] Re: a boring parser
Anatoly Yakovenko
aeyakovenko at gmail.com
Thu Oct 1 16:02:28 EDT 2009
so whats pretty cool is that I can traverse arbitrary data structures as well:
data Tree a = Tree (Tree a) a (Tree a) | Bottom
deriving Show
left a = do
make $ \ st -> do
case(st) of
(Bottom) -> eos
(Tree left val right) ->
case (a < val) of
True -> return $ (val, left)
False -> noMatch
right a = do
make $ \ st -> do
case(st) of
(Bottom) -> eos
(Tree left val right) ->
case (a > val) of
True -> return $ (val, right)
False -> noMatch
eqT a = do
make $ \ st -> do
case(st) of
(Bottom) -> eos
(Tree _ val _) ->
case (a == val) of
True -> return $ (val, st)
False -> noMatch
search a = manyTill (left a <|> right a) (eqT a)
> run (search 5) $ Tree (Tree Bottom 1 Bottom) 3 (Tree Bottom 5 Bottom)
Right (([3],5),Tree Bottom 5 Bottom)
On Wed, Sep 30, 2009 at 8:04 PM, Anatoly Yakovenko
<aeyakovenko at gmail.com> wrote:
> i got annoyed with Parsec and wrote a much more boring parser which
> allows me to parse anything with any kind of matching i want. Its
> basically a combination of State and Error monads.
>
> So i can use a grep like parser that matches via a regular expression
> over a list of lines
>
> grep re = do
> vv::B.ByteString <- any
> let (_,_,_,rv) = (vv =~
> re)::(B.ByteString,B.ByteString,B.ByteString,[B.ByteString])
> case (rv) of
> [] -> throwError "no match"
> _ -> return $ rv
>
>> run (grep $ C.pack "(hello)") $ [C.pack "hello world"]
> Right (["hello"],[])
>
> or use the same library to scan over a string by combining regular expressions
>
> regex re = do
> make $ \ st -> do
> case (B.null st) of
> True -> throwError "eos"
> _ -> do
> let (_,_,after,rv) = (st =~
> re)::(B.ByteString,B.ByteString,B.ByteString,[B.ByteString])
> case (rv) of
> [] -> throwError "no match"
> _ -> return $ (rv,after)
>
>
>
>> run (do aa <- regex $ C.pack "(hello)"; bb <- regex $ C.pack " (world)"; return (aa,bb) ) $ C.pack "hello world"
> Right ((["hello"],["world"]),"")
>
> or simply match integers in a list, or anything that is of type Eq
>
>> run (many1 $ eq 1) [1,1,1,2,3,4]
> Right ([1,1,1],[2,3,4])
>
> i can define lt
>
> lt cc = do
> vv <- any
> case (vv < cc) of
> True -> return $ vv
> _ -> throwError "no match"
>
> and do
>
>> run (many1 $ lt 5 <|> eq 5) [1..10]
> Right ([1,2,3,4,5],[6,7,8,9,10])
>
> here is the implementation
>
> module Parser( ParserM --type alias for the parser ParserM a b is
> over "stream" a and returns b
> , make --makes a parser from a matching function of
> type :: stream -> m (match_data,stream)
> --for example any is implemented via:
> --any :: ParserM [a] a
> --any = make $ \ ll ->
> -- case (ll) of
> -- (hh:tt) -> return $ (hh,tt)
> -- _ -> throwError "eos
> --matches and returns an element from a
> list, which makes any of type :: ParserM [a] a
> , any --matches any element from [a] type stream
> , eq --matches an equal element from [Eq] stream,
> trivialy implemented in terms of any
> --eq :: Eq a => a -> ParserM [a] a
> --eq cc = do
> -- vv <- any
> -- case (vv == cc) of
> -- True -> return $ vv
> -- _ -> throwError "no match
> , (<|>) --or operator, tries the left one then the right one
> , manyTill --collects the results of parser 1 until
> parser 2 succeeds
> , many1 --collects the results of the parser, must
> succeed at least once
> , many --collects the results of a parser
> , run --runs the parser
> ) where
>
> import Control.Monad.State.Lazy
> import Control.Monad.Error
> import Test.QuickCheck
> import Control.Monad.Identity
> import Prelude hiding (any)
>
> type ParserM a c = StateT a (ErrorT [Char] Identity) c
>
> make pp = do
> st <- get
> (rv,nst) <- pp $ st
> put $ nst
> return $ rv
>
> aa <|> bb = aa `catchError` \ _ -> bb
>
> manyTill :: ParserM a c -> ParserM a d -> ParserM a ([c],d)
> manyTill pp ee = do
> do dd <- ee
> return $ ([],dd)
> `catchError` \ _ -> do
> cc <- pp
> (ccs,dd) <- manyTill pp ee
> return $ (cc:ccs,dd)
>
> many1 pp = do
> rv <- pp
> rest <- many1 pp `catchError` \ _ -> return $ []
> return $ rv : rest
>
> many pp = do many1 pp
> <|> return []
>
>
> any :: ParserM [a] a
> any = make $ \ ll ->
> case (ll) of
> (hh:tt) -> return $ (hh,tt)
> _ -> throwError "eos"
>
> eq :: Eq a => a -> ParserM [a] a
> eq cc = do
> vv <- any
> case (vv == cc) of
> True -> return $ vv
> _ -> throwError "no match"
>
> lt cc = do
> vv <- any
> case (vv < cc) of
> True -> return $ vv
> _ -> throwError "no match"
>
> run pp dd = runIdentity $ runErrorT $ runStateT pp dd
> run' = flip run
>
>
> prop_MatchA = (Right ('a',"bc")) == (run' "abc" $ eq 'a')
> prop_MatchEOS = (Left "eos") == (run' "" $ eq 'a')
> prop_MatchNoMatch = (Left "no match") == (run' ("bcd") $ eq 'a')
>
> prop_MatchABC =(Right ('c',""))== (run' "abc" $ do eq 'a'
> eq 'b'
> eq 'c')
>
> prop_MatchA_C = (run' "abc" $ do eq 'a'
> eq 'd' <|> eq 'b' <|> any
> eq 'c') == (Right ('c',""))
>
> prop_Or = (run' "abc" $ do { eq 'a'
> ; do { eq 'b'
> ; eq 'd'
> }
> <|> do { eq 'b'
> ; eq 'c'
> }
> }) == (Right ('c',""))
>
> prop_UntilC = (Right (("",'c'),"")) == (run' ("c") $ manyTill any $ eq 'c')
>
> prop_Until1 ls =
> let rv = run' (ls ++ [1]) $ manyTill any $ eq 1
> in case (rv) of
> Right ((ls,1),rest) -> (elem 1 ls) == False
> _ -> False
>
> prop_all1 ls =
> let rv = run' ([1,1,1] ++ ls) $ many1 $ eq 1
> in case (rv) of
> Right (_,(1:_)) -> False
> Right ((1:1:1:_),_) -> True
> _ -> False
>
More information about the Haskell-Cafe
mailing list