module Ivory.BitData.Parser (
parseDefs, parseBitLiteral
) where
import Control.Applicative
import Control.Monad (when)
import Data.Char (isUpper, isLower, toLower, digitToInt)
import Data.Maybe (listToMaybe)
import Numeric (readDec, readHex, readInt)
import Text.Parsec ((<?>), chainl1, many1, sepBy, sepBy1, eof,
oneOf, option, digit, hexDigit,
unexpected, notFollowedBy, letter, alphaNum)
import Text.Parsec.String (Parser)
import Ivory.BitData.AST
import Ivory.BitData.TokenParser
liftReadS :: ReadS a -> String -> Parser a
liftReadS f = maybe (unexpected "no parse") (return . fst) .
listToMaybe . filter (null . snd) . f
binDigit :: Parser Char
binDigit = oneOf "01"
readBin :: (Eq a, Num a) => ReadS a
readBin = readInt 2 (`elem` "01") digitToInt
digitParser :: Char -> Parser Int
digitParser 'b' = many1 binDigit >>= liftReadS readBin
digitParser 'd' = many1 digit >>= liftReadS readDec
digitParser 'x' = many1 hexDigit >>= liftReadS readHex
digitParser _ = fail "invalid bit literal base character"
bitLiteral :: Parser BitLiteral
bitLiteral = (lexeme $ do
size <- many1 digit >>= liftReadS readDec
(bitLiteralTail size <|> return (BitLitUnknown size))
<* notFollowedBy alphaNum) <?> "bit literal"
bitLiteralTail :: Int -> Parser BitLiteral
bitLiteralTail size = do
baseChar <- toLower <$> letter
value <- digitParser baseChar
if size == 0
then return $ BitLitUnknown value
else do
when (value >= 2 ^ size) $
fail "bit literal out of range"
return $ BitLitKnown size value
parseBitLiteral :: Parser BitLiteral
parseBitLiteral = whiteSpace *> bitLiteral <* eof
typeName :: Parser String
typeName = do
x <- identifier
if not (isUpper (head x))
then fail "expecting type or type constructor name"
else return x
valueName :: Parser String
valueName = valueIdentifier <|> (symbol "_")
where valueIdentifier = do
x <- identifier
if not (isLower (head x))
then fail "expecting value name"
else return x
parseType1 :: Parser BitTy
parseType1 = numTyLit <|> conT <|> parens parseType
where numTyLit = TyNat <$> natural
conT = TyCon <$> typeName
parseType :: Parser BitTy
parseType = parseType1 `chainl1` (whiteSpace >> return TyApp)
parseDefs :: Parser [Def]
parseDefs = whiteSpace *> some parseDef <* eof
parseDef :: Parser Def
parseDef =
Def <$ symbol "bitdata"
<*> typeName
<* reservedOp "::"
<*> parseType
<* reservedOp "="
<*> sepBy1 parseConstr (reservedOp "|")
parseConstr :: Parser Constr
parseConstr =
Constr <$> valueName
<*> parseFields
<*> parseLayout
parseFields :: Parser [Field]
parseFields = option [] (braces (sepBy parseField comma))
parseLayout :: Parser Layout
parseLayout = option [] (symbol "as" *> body)
where body = sepBy1 item (reservedOp "#")
item = LayoutConst <$> bitLiteral
<|> LayoutField <$> valueName
parseField :: Parser Field
parseField =
Field <$> valueName
<* reservedOp "::"
<*> parseType