ewe-0.1.0.12: src/Language/EWE/Parser.hs
module Language.EWE.Parser where
import System.IO
import Control.Monad
import Text.Parsec
import Text.Parsec.Prim
import Text.ParserCombinators.Parsec hiding(try)
import Language.EWE.AbsSyn
import Language.EWE.EweLang(eweLangDef)
import qualified Language.EWE.Token as Tkn
-- Parser generates from the language
pEweLexer = Tkn.makeTokenParser eweLangDef
pIdentifier = Tkn.identifier pEweLexer
pReserved = Tkn.reserved pEweLexer
pReservedOp = Tkn.reservedOp pEweLexer
pParens = Tkn.parens pEweLexer
pBrackets = Tkn.brackets pEweLexer
pInteger = Tkn.integer pEweLexer
pComma = Tkn.comma pEweLexer
pWhiteSpace = Tkn.whiteSpace pEweLexer
pSymbol = Tkn.symbol pEweLexer
pLexeme = Tkn.lexeme pEweLexer
-- Parser of EWE Language
pEWE:: String -> String -> Either ParseError Prog
pEWE inp name = parse pEweProg name inp
pEweProg :: Parser Prog
pEweProg = pStmts
pStmts :: Parser Prog
pStmts = do res <- sepEndBy pStmtLine pEOL
let (eq',st') = foldr f ([],[]) res
return $ Prg st' eq'
where f (e,s) (e',s') = (e++e',s:s')
pLabel :: Parser String
pLabel = do id <- pId
char ':'
return $ id
<?> "Parsing label"
pStmtLine :: Parser (Equates, Stmt)
pStmtLine = do eqs <- pEquates
return $ (eqs, Stmt [] INI)
<|>
do l <- pLabels
char '\t'
ins <- pInstr
return $ ([], Stmt l ins)
<|>
do pWhiteSpace
return $ ([], Stmt [] INI)
pLabels :: Parser [String]
pLabels = pLabel `sepBy` pWhiteSpace
-- Instructions parsers
-- <instr> ::= ...
pInstr :: Parser Instr
pInstr = do instr <- choice [pPCInstr
,pReadInt
,pReadStr
,pWriteInt
,pWriteStr
,pGoto
,pIf
,pOneKw
,pPrefixMRef
]
return instr
-- "PC" ":=" <memref>
-- AbsSyn SPC (Set PC instruction)
pPCInstr :: Parser Instr
pPCInstr = do pReserved"PC"
pReservedOp ":="
mr <- pMRef2
return $ SPC mr
-- "readInt" "(" <memref> ")"
-- AbsSyn IRI Instruction Read Int
-- "readStr" "(" <memref> ")"
-- AbsSyn IRS Instruction Read String
-- pRead :: Parser Instr
-- pRead = do string "read"
-- (string "Int" >> pReadInt) <|> (string "Str" >> pReadStr)
-- pReadInt :: Parser Instr
-- pReadInt = pInstr1MRef IRI
-- pReadStr :: Parser Instr
-- pReadStr = do pSpaces
-- (mr1, mr2) <- pTokenB pReadMRefs
-- pSpaces
-- return $ IRS mr1 mr2
pReadInt :: Parser Instr
pReadInt = do pReserved "readInt"
mr <- pParens pMRef2
return $ IRI mr
pReadStr :: Parser Instr
pReadStr = do pReserved "readStr"
(mr1,mr2) <- pParens p
return $ IRS mr1 mr2
where p = do { mr1 <- pMRef2 ; pComma; mr2 <- pMRef2 ; return $ (mr1,mr2) }
-- "writeStr" "(" <memref> ")"
-- AbsSyn IWS Instruction Write String
-- "writeInt" "(" <memref> ")"
-- AbsSyn IWI Instruction Write Int
-- pWrite :: Parser Instr
-- pWrite = do string "write"
-- (string "Int" >> pWriteInt) <|> (string "Str" >> pWriteStr)
-- pWriteInt :: Parser Instr
-- pWriteInt = pInstr1MRef IWI
-- pWriteStr :: Parser Instr
-- pWriteStr = pInstr1MRef IWS
pWriteInt :: Parser Instr
pWriteInt = do pReserved "writeInt"
mr <- pParens pMRef2
return $ IWI mr
pWriteStr :: Parser Instr
pWriteStr = do pReserved "writeStr"
mr <- pParens pMRef2
return $ IWS mr
-- "goto" Integer
-- "goto" Identifier
-- AbsSyn IGI Goto to line
-- AbsSyn IGS Goto to Symbol
pGoto:: Parser Instr
pGoto = do pReserved "goto"
iOrId <- pTknIntOrId
let instr = either IGI IGS iOrId
return instr
-- "if" <mref> <condition> <memref> "then" "goto" Integer
-- "if" <mref> <condition> <memref> "then" "goto" Identifier
-- AbsSyn IFI If Cond then Int
-- AbsSyn IFS If Cont then Id
pIf :: Parser Instr
pIf = do pReserved "if"
mr1 <- pMRef2
cnd <- pCond
mr2 <- pMRef2
pReserved "then"
pReserved "goto"
iOrId <- pTknIntOrId
let instr = either (IFI mr1 cnd mr2) (IFS mr1 cnd mr2) iOrId
return instr
-- "halt"
-- "break"
-- IH Instruction Halt
-- IB Instruciton Break
listOneKw :: [(String,Instr)]
listOneKw = [("halt", IH), ("break", IB)]
pOneKw :: Parser Instr
pOneKw = choice
$ map (\(s,r) -> (pReserved s >> return r)) listOneKw
pReadMRefs :: Parser (MRef,MRef)
pReadMRefs = do pParens (do mr1 <- pMRef2
pComma
mr2 <- pMRef2
return $ (mr1, mr2))
pInstr1MRef :: (MRef -> Instr) -> Parser Instr
pInstr1MRef f =
do mr <- pParens pMRef2
return $ f mr
pPrefixMRef :: Parser Instr
pPrefixMRef =
do pReserved "M"
op <- pBrackets pMRefOrIdx'
pReservedOp ":="
either pIMMR pNextRHS op
<|>
(pLexeme pId >>= \id -> pReservedOp ":=" >> (pNextRHS (MRefId id)))
pIMMR :: (MRef,Int) -> Parser Instr
pIMMR (mr1,i) =
do pReservedOp ":="
mr2 <- pMRef2
return $ IMMR mr1 i mr2
pNextRHS :: MRef -> Parser Instr
pNextRHS mr1 =
(pLexeme pInt >>= \i -> return $ IMMI mr1 i)
<|>
(pLexeme pId >>= \id -> pEndRHS mr1 (MRefId id))
<|>
(pLexeme pStrLit >>= \str -> return $ IMMS mr1 str)
<|>
do pReserved "M"
op <- pBrackets (pTokenB pMRefOrIdx')
either (\(mr2,i) -> return $ IMRI mr1 mr2 i) (pEndRHS mr1) op
pEndRHS :: MRef -> MRef -> Parser Instr
pEndRHS mr1 mr2 = option (IMMM mr1 mr2) (pPartialArith mr1 mr2)
listAOps :: [(String, MRef -> MRef -> MRef -> Instr)]
listAOps = [("+",IAdd), ("-",ISub), ("*",IMul), ("/",IDiv), ("%",IMod)]
pPartialArith :: MRef -> MRef -> Parser Instr
pPartialArith mr1 mr2 =
choice $ map g listAOps
where g (s,f) = pReservedOp s
>> pMRef2
>>= \mr3 -> return $ f mr1 mr2 mr3
pMRefOrIdx' :: Parser (Either (MRef,Int) MRef)
pMRefOrIdx' = do mr <- pMRef2
pReservedOp "+"
i <- pInteger -- i <- pInt
return $ Left (mr,(fromInteger i))
<|>
(pInteger >>= \i -> return $ Right (MRefI (fromInteger i)))
-- Helpers parser to identify an integer or identifier
pTknIntOrId :: Parser (Either Int String)
pTknIntOrId = (pLexeme pInt >>= return.Left) <|> (pLexeme pId >>= return.Right)
pInt :: Parser Int
pInt = do l <- oneOf "123456789"
ls <- many digit
return $ (read (l:ls))
<|>
do l <- oneOf "0"
notFollowedBy digit
return $ (read (l:[]))
pId :: Parser String
pId = do l <- (letter <|> char '_')
ls <- many (alphaNum <|> char '_')
return $ l:ls
-- String Literal Parser
pStrLit :: Parser String
pStrLit = between (pSymbol "\"") (pSymbol "\"") (many $ p)
<|>
between (pSymbol "'") (pSymbol "'") (many $ p)
where p = oneOf " :!#$%&*+./<=>?@\\^|-~()[]" <|> digit <|> letter
-- Parser candidates to delete, because are deprecated
pToken :: Parser a -> Parser a
pToken p = (p >>= \a -> many (char ' ') >> return a )
pTokenB :: Parser a -> Parser a
pTokenB p = do many (char ' ')
a <- p
many (char ' ')
return a
pSpaces :: Parser ()
pSpaces = many (char ' ') >> return ()
-- MemRef
pMRef2 :: Parser MRef
pMRef2 = do pReserved "M"
i<- pBrackets pInteger
return $ (MRefI (fromInteger i))
<|>
(pIdentifier >>= (return.MRefId))
<?>
"pMRef2"
-- Equates
pEqu :: Parser Equ
pEqu = do pReserved "equ"
id <- pIdentifier
pReserved "M"
i <- pBrackets pInteger
return $ (id,(fromInteger i))
pEquates :: Parser Equates
pEquates = do{ equates <- sepBy pEqu (pWhiteSpace <|> pEOL); eof; return equates }
-- Cond
listCond :: [(String,Cond)]
listCond = [(">",CLT)
,(">=",CLET)
,("<",CGT)
,("<=",CGET)
,("=",CE)
,("<>",CNE)]
pCond :: Parser Cond
pCond = choice $ map (\(s,r) -> (pReservedOp s >> return r)) listCond
-- End of Line parser
pEOL :: Parser ()
pEOL = (char '\n' >> return ()) <|> (string "\r\n" >> return ())
-- Basic Parsers for compiling assemble files
pOneLineComment :: Parser ()
pOneLineComment = do try (string "#")
skipMany (satisfy (/= '\n'))
char '\n'
return ()
pEmptyLine :: Parser ()
pEmptyLine = do pWhiteSpace
char '\n'
return ()