--------------------------------------------------
-- Copyright 1994 by Peter Thiemann
-- $Log: Parsers.hs,v $
-- Revision 1.1 2004/08/05 11:11:58 malcolm
-- Add a regression testsuite for the nhc98 compiler. It isn't very good,
-- but it is better than nothing. I've been using it for about four years
-- on nightly builds, so it's about time it entered the repository! It
-- includes a slightly altered version of the nofib suite.
-- Instructions are in the README.
--
-- Revision 1.1 1996/01/08 20:02:32 partain
-- Initial revision
--
-- Revision 1.3 1994/03/15 15:34:53 thiemann
-- minor revisions
--
--Revision 1.2 1993/08/31 12:31:32 thiemann
--reflect changes in type FONT
--
--Revision 1.1 1993/08/17 12:34:29 thiemann
--Initial revision
--
-- $Locker: $
--------------------------------------------------
module Parsers where
infixl 6 `using`, `using2`
infixr 7 `alt`
infixr 8 `thn`, `xthn`, `thnx`
type Parser a b = [a] -> [(b, [a])]
succeed :: beta -> Parser alpha beta
succeed value tokens = [(value, tokens)]
-- the parser
-- satisfy p
-- accepts the language { token | p(token) }
satisfy :: (alpha -> Bool) -> Parser alpha alpha
satisfy p [] = []
satisfy p (token:tokens) | p token = succeed token tokens
| otherwise = []
-- the parser
-- literal word
-- accepts { word }
literal :: Eq alpha => alpha -> Parser alpha alpha
literal token = satisfy (== token)
-- if p1 and p2 are parsers accepting L1 and L2 then
-- then p1 p2
-- accepts L1.L2
thn :: Parser alpha beta -> Parser alpha gamma -> Parser alpha (beta, gamma)
thn p1 p2 =
concat
. map (\ (v1, tokens1) -> map (\ (v2, tokens2) -> ((v1,v2), tokens2)) (p2 tokens1))
. p1
thnx :: Parser alpha beta -> Parser alpha gamma -> Parser alpha beta
thnx p1 p2 =
concat
. map (\ (v1, tokens1) -> map (\ (v2, tokens2) -> (v1, tokens2)) (p2 tokens1))
. p1
xthn :: Parser alpha beta -> Parser alpha gamma -> Parser alpha gamma
xthn p1 p2 =
concat
. map (\ (v1, tokens1) -> map (\ (v2, tokens2) -> (v2, tokens2)) (p2 tokens1))
. p1
-- if p1 and p2 are parsers accepting L1 and L2 then
-- alt p1 p2
-- accepts L1 \cup L2
alt :: Parser alpha beta -> Parser alpha beta -> Parser alpha beta
alt p1 p2 tokens = p1 tokens ++ p2 tokens
-- if p1 is a parser then
-- using p1 f
-- is a parser that accepts the same language as p1
-- but mangles the semantic value with f
using :: Parser alpha beta -> (beta -> gamma) -> Parser alpha gamma
using p1 f = map (\ (v, tokens) -> (f v, tokens)) . p1
using2 :: Parser a (b,c) -> (b -> c -> d) -> Parser a d
using2 p f = map ( \((v,w), tokens) -> (f v w, tokens)) . p
-- if p accepts L then plus p accepts L+
plus :: Parser alpha beta -> Parser alpha [beta]
plus p = (p `thn` rpt p) `using2` (:)
-- if p accepts L then rpt p accepts L*
rpt :: Parser alpha beta -> Parser alpha [beta]
rpt p = plus p `alt` succeed []
-- if p accepts L then opt p accepts L?
opt :: Parser alpha beta -> Parser alpha [beta]
opt p = (p `using` \x -> [x]) `alt` succeed []
-- followedBy p1 p2 recognizes L(p1) if followed by a word in L (p2)
followedBy :: Parser a b -> Parser a c -> Parser a b
followedBy p q tks = [(v, rest) | (v, rest) <- p tks, x <- q rest]
|