packages feed

cparsing-0.1.0.0: MiniC/Parser/Lexical.hs

{-# LANGUAGE ViewPatterns, ScopedTypeVariables #-}

-- | Lexer for C. Parts from 'Text.Parsec.Token' have been copied here, because they
-- could not handle source information, or literal type specification, so it may
-- be considered as a reimplementation of common lexical elements.
module MiniC.Parser.Lexical where

import MiniC.AST
import MiniC.Instances
import MiniC.Parser.Base
import MiniC.Representation

import Data.Char (digitToInt)
import Control.Lens
import Control.Applicative hiding ((<|>), many, optional)
import Text.Parsec
import Text.Parsec.Token ( GenLanguageDef(..))
import qualified Text.Parsec.Token as P

cStyle :: Monad m => GenLanguageDef CStream u m
cStyle = LanguageDef
           { commentStart    = "/*"
           , commentEnd      = "*/"
           , commentLine     = "//"
           , nestedComments  = False
           , identStart      = letter <|> oneOf "_"
           , identLetter	 = alphaNum <|> oneOf "_"
           , opStart         = oneOf ""
           , opLetter        = oneOf ""
           , reservedOpNames = []
           , reservedNames   = []
           , caseSensitive   = True
           }
           
cDef :: Monad m => GenLanguageDef CStream u m
cDef = cStyle 
         { reservedNames = [ "asm", "auto", "break", "bool", "case", "char", "const", "continue"
                           , "default", "do", "double", "else", "enum", "extern"
                           , "float", "for", "goto", "if", "int", "long", "register"
                           , "return", "restrict", "short", "signed", "sizeof", "static"
                           , "struct" , "switch", "typedef", "typeof", "union", "unsigned", "void"
                           , "volatile", "while" 
                           ]
         }

lexer :: Monad m => P.GenTokenParser CStream u m
lexer = P.makeTokenParser cDef    
  
  

whole :: CParser a -> CParser a
whole p = whiteSpace *> p <* eof
  
-- * Inherited tokens
lexeme :: Monad m => ParsecT CStream u m a -> ParsecT CStream u m a
lexeme = P.lexeme lexer

symbol :: Monad m => String -> ParsecT CStream u m String
symbol = P.symbol lexer
  
parens, braces, brackets :: Monad m => ParsecT CStream u m a -> ParsecT CStream u m a
parens = P.parens lexer
braces = P.braces lexer
brackets = P.brackets lexer

identifier = lexeme $ withInfo $ Ident <$> noWSIdentifier

simpleIdentifier :: Monad m => ParsecT String u m String
simpleIdentifier = lexeme noWSIdentifier

noWSIdentifier :: forall u m . Monad m => ParsecT String u m String
noWSIdentifier = try $
     do name <- identOrReserved
        if name `elem` reservedNames (cDef :: GenLanguageDef CStream u m)
         then unexpected ("reserved word " ++ show name)
         else return name

identOrReserved :: Monad m => ParsecT String u m String
identOrReserved = (:) <$> identStart cDef <*> many (identLetter cDef) <?> "identifier"

reserved :: Monad m => String -> ParsecT String u m ()
reserved = P.reserved lexer

-- | This function decides which symbol can follow another symbol while the whole lexeme remains
-- a valid C operator.
opNoFollow op 
  = concat $ ["=" | op `elem` ["+","-","*","/","%","&","^","|","<",">","<<",">>"]]
               ++ [op | op `elem` ["&","|","+","-",">","<"]]
               ++ [">" | op == "-"]
               
reservedOp :: String -> CParser ()
reservedOp name 
  = try $ lexeme (string name) 
            >> (notFollowedBy (oneOf $ opNoFollow name) <?> ("end of " ++ show name))

whiteSpace :: Monad m => ParsecT String u m ()
whiteSpace = P.whiteSpace lexer

comma, colon, semicolon, openParen, closeParen
  , openBrace, closeBrace, openBracket, closeBracket :: Monad m => ParsecT String u m String
comma = symbol ","
colon = symbol ":"
semicolon = symbol ";"
openParen = symbol "("
closeParen = symbol ")"
openBrace = symbol "{"
closeBrace = symbol "}"
openBracket = symbol "["
closeBracket = symbol "]"

---------------------
-- number literals --
---------------------

-- integer literals
integer :: CParser LiteralBI
integer = lexeme $ withInfo $ IntLit <$> (sign <*> nat) <*> integerSuffix

nat = zeroNumber <|> decimal
int = lexeme sign <*> nat
sign = (char '-' *> return negate) <|> (optional (char '+') *> return id)
zeroNumber = char '0' *> (hexadecimal <|> binary <|> octal <|> return 0) <?> ""

decimal = P.decimal lexer
hexadecimal = P.hexadecimal lexer
octal = number 8 octDigit
binary = oneOf "bB" *> number 2 (oneOf "01")

-------------------------------
-- * floating point literals --
-------------------------------

float :: CParser LiteralBI
float = lexeme floating <?> "float literal"

floating = withInfo $ FloatLit 
  <$> ( try (char '0' *> oneOf "xX" *> floatingBase hexDigit 16 (oneOf "pP"))
           <|> floatingBase digit 10 (oneOf "eE")) 
  <*> optionMaybe fractSuffix 

floatingBase :: CParser Char -> Integer -> CParser a -> CParser Rational
floatingBase digit baseNum exponentSign 
  = try ( combine (option 0 integralPart) (char '.' *> fractionPart) (option 1 exponentPart) )
      <|> try ( combine (integralPart <* char '.') (option 0 fractionPart) (option 1 exponentPart) )
      <|> combine integralPart (option 0 (char '.' *> fractionPart)) exponentPart
  where combine ip fp ep = (*) <$> ( (+) <$> ip <*> fp ) <*> ep
        integralPart = fromIntegral <$> number baseNum digit
        fractionPart = fractNumber baseNum digit
        exponentPart = exponentSign *> ((fromIntegral baseNum ^) <$> (sign <*> decimal))
               
fractSuffix 
  = (oneOf "fF" *> return LitFloat) <|> (oneOf "lL" *> return LitLongDouble)
               
integerSuffix = do suffs <- many (intSignSuffix <|> intSizeSuffix)
                   return $ foldl (flip ($)) (IntLitSpec Nothing Nothing) suffs 
                 
intSignSuffix = try (oneOf "uU") *> return (set intLitSign (Just LitUnsigned) )

intSizeSuffix = (try (string "ll" <|> string "LL") *> return (set intLitSize (Just LitLongLong)))
                  <|> (try (oneOf "lL") *> return (set intLitSize (Just LitLong)))

number :: Monad m => Integer -> ParsecT CStream u m Char -> ParsecT CStream u m Integer
number base baseDigit
    = do{ digits <- many1 baseDigit
        ; let n = foldl (\x d -> base*x + toInteger (digitToInt d)) 0 digits
        ; seq n (return n)
        }

fractNumber :: Integer -> CParser Char -> CParser Rational
fractNumber (fromIntegral -> base) baseDigit
    = do{ digits <- many1 baseDigit
        ; let (n,e) = foldl (\(x,e) d -> (x + (fromIntegral (digitToInt d) / e), e * base)) (0,base) digits
        ; seq n (return n)
        }

hexaNum :: Monad m => ParsecT CStream u m Integer
hexaNum = number 16 hexDigit
                 
--------------------------------
-- * Char and String literals --
--------------------------------
         
unicodePrefix = option False (oneOf "lL" *> return True)      

charLiteral     
  = withInfo ( lexeme $ CharLit <$> unicodePrefix
                 <*> simpleCharLiteral )
      <?> "character"

simpleCharLiteral :: Monad m => ParsecT CStream u m Char
simpleCharLiteral = between (char '\'')
                            (char '\'' <?> "end of character")
                                characterChar

characterChar :: Monad m => ParsecT CStream u m Char                                
characterChar   = do c <- (Just <$> charLetter) <|> escapeCode
                     case c of Just x -> return x
                               Nothing -> characterChar
                <?> "literal character"

charLetter :: Monad m => ParsecT CStream u m Char                                                
charLetter = satisfy (\c -> (c /= '\'') && (c /= '\\') && (c > '\026'))

stringLiteral 
  = withInfo ( StringLiteral <$> unicodePrefix
                             <*> simpleStringLiteral
                             <?> "literal string")

simpleStringLiteral :: Monad m => ParsecT CStream u m String
simpleStringLiteral 
  = lexeme $ foldr (maybe id (:)) ""
          <$> between (char '"') (char '"' <?> "end of string")
                      (many stringChar)

stringChar :: Monad m => ParsecT CStream u m (Maybe Char)
stringChar = (Just <$> stringLetter) <|> escapeCode <?> "string character"

stringLetter :: Monad m => ParsecT CStream u m Char
stringLetter = satisfy (\c -> ((c /= '"') && (c /= '\\') && (c > '\026')) || c == '\n')

-- escape codes
escapeCode :: Monad m => ParsecT CStream u m (Maybe Char)                                
escapeCode = char '\\' *> ((Just <$> try charNum) <|> charEsc) <?> "escape code"

charNum :: Monad m => ParsecT CStream u m Char
charNum = (toEnum . fromInteger) 
            <$> ((char '0' *> number 8 octDigit)
                   <|> (oneOf "xX" *> number 16 hexDigit)
                   <|> (oneOf "uU" *> number 16 hexDigit))
    
charEsc :: Monad m => ParsecT CStream u m (Maybe Char)                                
charEsc = choice (map (\(c,code) -> char c *> return code) escMap)

escMap          = zip "abfnrtv\\\"\'0\n" (map Just "\a\b\f\n\r\t\v\\\"\'\0" ++ [Nothing])