{-# LANGUAGE TemplateHaskell #-} -- | -- Module : Data.Binding.Hobbits.PatternParser -- Copyright : (c) 2011 Edwin Westbrook, Nicolas Frisby, and Paul Brauner -- -- License : BSD3 -- -- Maintainer : emw4@rice.edu -- Stability : experimental -- Portability : GHC -- -- A simple parser for Haskell patterns. Currently does not handle: -- -- - Record patterns @Pt { pointx = x }@ -- -- - Signature patterns @p :: t@ -- -- - View patterns @e -> p@ module Data.Binding.Hobbits.PatternParser (parsePattern, parseVar) where import Text.ParserCombinators.Parsec import Language.Haskell.TH import Data.Char varStartChars = ['a'..'z'] ctorStartChars = ['A'..'Z'] identChars = varStartChars ++ ctorStartChars ++ ['0'..'9'] ++ "'_" infixChars = "!#$%&*+./<=>?@" varParser :: GenParser Char st String varParser = do char1 <- oneOf varStartChars char_rest <- many (oneOf identChars) return (char1 : char_rest) ctorParser :: GenParser Char st String ctorParser = do char1 <- oneOf ctorStartChars char_rest <- many (oneOf identChars) return (char1 : char_rest) infixParser :: GenParser Char st String infixParser = do char1 <- char ':' char_rest <- many (oneOf infixChars) return (char1 : char_rest) stringParser :: GenParser Char st String stringParser = do char '"' res <- stringContentsParser return res stringContentsParser = many (noneOf "\\\"") >>= \prefix -> (char '"' >> return prefix) <|> (char '\\' >> do c <- anyChar rest <- stringContentsParser return $ prefix ++ [c] ++ rest) charParser :: GenParser Char st Char charParser = do char '\'' c <- ((char '\\' >> anyChar) <|> anyChar) char '\'' return c digitsToInt digits = helper digits 0 where helper [] accum = accum helper (digit:digits) accum = helper digits (accum * 10 + (digitToInt digit)) intToRational :: Int -> Rational intToRational = fromIntegral digitsToFrac digits = helper digits where helper [] = 0.0 helper (digit:digits) = ((helper digits) + (intToRational $ digitToInt digit)) / 10 numParser :: GenParser Char st Lit numParser = do base_digits <- many1 (oneOf ['0'..'9']) ((do char '.' frac_digits <- many1 (oneOf ['0'..'9']) return (RationalL $ (intToRational $ digitsToInt base_digits) + digitsToFrac frac_digits)) <|> return (IntegerL $ fromIntegral $ digitsToInt base_digits)) litParser :: GenParser Char st Lit litParser = (charParser >>= return . CharL) <|> (stringParser >>= return . StringL) <|> numParser commaSepParser :: GenParser Char st [Pat] commaSepParser = (do first <- pattParser 0 rest <- (char ',' >> commaSepParser) <|> (return []) return (first:rest)) <|> (return []) -- the int gives the "level": -- 0 = parse anything -- 1 = parse ctor args but not infix ops -- 2 = do not parse ctor args or infix ops tokenParser :: Int -> GenParser Char st Pat tokenParser i = -- literals (litParser >>= return . LitP) <|> -- wildcards (char '_' >> return WildP) <|> -- bangs (do char '!' patt <- pattParser i return $ BangP patt) <|> -- tildes (do char '~' patt <- pattParser i return $ TildeP patt) <|> -- as-patterns (try (do var <- varParser wsParser char '@' patt <- pattParser i return $ AsP (mkName var) patt)) <|> -- vars (varParser >>= return . VarP . mkName) <|> -- tuples; NOTE: we parse any parenthesized expression as a tuple, -- and remove the TupP constructor when there are no commas (do char '(' tup <- commaSepParser char ')' return (case tup of [] -> ConP '() [] [patt] -> patt _ -> TupP tup)) <|> -- constructor applications (do ctor <- ctorParser args <- if i < 2 then many (try $ pattParser 2) else return [] return $ ConP (mkName ctor) args) <|> -- lists (do char '[' elems <- commaSepParser char ']' return $ ListP elems) wsParser :: GenParser Char st () wsParser = many (oneOf " \t\n\r") >> return () pattParser :: Int -> GenParser Char st Pat pattParser i = do wsParser res <- if i == 0 then -- infix constructor applications try (do lhs <- pattParser 1 op <- infixParser rhs <- pattParser 0 return $ ConP (mkName op) [lhs, rhs]) <|> tokenParser i else tokenParser i wsParser return res varOnlyParser :: GenParser Char st String varOnlyParser = do wsParser res <- varParser wsParser eof return res ---------------------------------------- -- Finally, the external interface... -- ---------------------------------------- -- | Parse a string into a Template Haskell pattern. parsePattern str = case parse (pattParser 0) "" str of Left err -> error $ show err Right patt -> patt {-| Parse a string for a Haskell variable; return the string on success (if the string is a valid Haskell variable) and signal an error otherwise. -} parseVar str = case parse varOnlyParser "" str of Left err -> error $ show err Right str -> str