module Parser ( readTerm, readProcess, readProcesses) where import Control.Monad (liftM) import Control.Monad.Error (throwError) import Text.ParserCombinators.Parsec import TypDefs (Condition (..), PiProcess (..), Term (..), Type(..), Value(..), Name, PiError(Parser), ThrowsError) parseNull :: Parser PiProcess parseNull = char '0' >> return Null "parse null" parseIn :: Parser PiProcess parseIn = do _ <- string "in(" name <- parseTerm paddedComma var <- parseTerm _ <- char ')' parseSeq $ In name var "parse in" parseOut :: Parser PiProcess parseOut = do _ <- string "out(" name <- parseTerm paddedComma term <- parseTerm _ <- char ')' parseSeq $ Out name term "parse out" parseReplicate :: Parser PiProcess parseReplicate = do _ <- string "!(" process <- parseProcess _ <- char ')' return $ Replicate process "parse replicate" myOption :: Show b => a -> Parser a -> Parser b -> Parser a myOption opt parser sep = try (notFollowedBy sep >> return opt) <|> (sep >> parser) myOptionMaybe :: Show b => Parser a -> Parser b -> Parser (Maybe a) myOptionMaybe parser = myOption Nothing (liftM Just parser) parseSeq :: PiProcess -> Parser PiProcess parseSeq p1 = do p2 <- myOption Null parseProcess (paddedChar ';') return $ p1 `Seq` p2 parseNew :: Parser PiProcess parseNew = do _ <- string "new" spaces name <- parseTerm parseSeq $ New name "parse new" parseIf :: Parser PiProcess parseIf = do _ <- string "if" spaces cond <- parseCondition paddedStr "then" p1 <- parseProcess p2 <- myOption Null parseProcess (paddedStr1 "else") return $ If cond p1 p2 "parse if" parseLet :: Parser PiProcess parseLet = do _ <- string "let" spaces name <- parseTerm paddedChar1 '=' val <- try (liftM Proc parseProcess) <|> liftM Term parseTerm p <- myOptionMaybe parseProcess (paddedStr1 "in") return $ Let name val p "parse let" parseAtom :: Parser PiProcess parseAtom = do _ <- char '&' atom <- parseTerm parseSeq $ Atom atom padded1 :: Parser a -> Parser () padded1 p = do skipMany1 space _ <- p skipMany1 space paddedChar :: Char -> Parser () paddedChar ch = padded $ char ch paddedStr :: String -> Parser () paddedStr str = padded $ string str padded :: Parser a -> Parser () padded p = do spaces _ <- p spaces paddedChar1 :: Char -> Parser () paddedChar1 ch = padded1 $ char ch paddedStr1 :: String -> Parser () paddedStr1 str = padded1 $ string str paddedComma :: Parser () paddedComma = paddedChar ',' parseCondition :: Parser Condition parseCondition = do t1 <- parseTerm paddedChar1 '=' t2 <- parseTerm return $ t1 `Equals` t2 parseTVar :: Parser Term parseTVar = do v <- readVar case v of "true" -> return $ TBool True "false" -> return $ TBool False _ -> do t <- myOptionMaybe parseType (paddedChar ':') return $ TVar v t parseTFun :: Parser Term parseTFun = do name <- readVar spaces args <- bracketed $ sepBy parseTerm paddedComma return $ case (name,args) of ("pair", t1:t2:_) -> TPair (t1,t2) ("list", _ ) -> TList args _ -> TFun name args parseTStr :: Parser Term parseTStr = do _ <- char '"' x <- many $ noneOf "\"" _ <- char '"' return $ TStr x parseTNum :: Parser Term parseTNum = liftM (TNum . read) (many1 digit) readVar :: Parser Name readVar = do frst <- letter <|> symbol rest <- many $ letter <|> digit <|> symbol return $ frst:rest symbol :: Parser Char symbol = oneOf "'._<>" parseTerm :: Parser Term parseTerm = try parseAnonChan <|> try parseTFun <|> parseTNum <|> parseTVar <|> parseTStr where parseAnonChan = do _ <- char '{' spaces arg <- many parseTerm spaces _ <- char '}' return $ TFun "anonChan" arg parseProcesses :: Parser [PiProcess] parseProcesses = sepEndBy parseProcess newline parseProcess :: Parser PiProcess parseProcess = liftM (\ps -> case ps of [p] -> p _ -> Conc ps) $ sepBy1 parseProcess' (char '|') where parseProcess' = bracketed parseProcess'' <|> parseProcess'' parseProcess'' = parseNull <|> try parseIf <|> parseIn <|> parseOut <|> parseLet <|> parseReplicate <|> parseNew <|> parseAtom parseType :: Parser Type parseType = try (str HttpRequest) <|> try (str HttpResponse) -- <|> try (str Header) -- <|> parseListType where str t = string (show t) >> return t --parseListType = string "List" >> many1 space >> fmap List parseType bracketed :: Parser a -> Parser a bracketed = between (char '(') (char ')') readOrThrow :: Parser a -> String -> String -> ThrowsError a readOrThrow parser name input = case parse parser name input of Left err -> throwError $ Parser err Right val -> return val readProcess :: String -> ThrowsError PiProcess readProcess = readOrThrow parseProcess "single process" readProcesses :: String -> ThrowsError [PiProcess] readProcesses = readOrThrow parseProcesses "multiple-processes" readTerm :: String -> ThrowsError Term readTerm = readOrThrow parseTerm "term"