packages feed

lsystem-1.2: src/ParserLS.hs

{-# LANGUAGE FlexibleContexts, RankNTypes #-}
module ParserLS where

import Text.ParserCombinators.UU
import Text.ParserCombinators.UU.BasicInstances
import Text.ParserCombinators.UU.Utils
import SistemaL
import Data.Char
import Data.Maybe
import Data.List
import Text.Printf

-- %% Parser Interface %%
--execParser' :: Parser a -> String -> (a, [Error LineColPos])
--execParser' p = parse ((,) <$> p <*> pEnd) . createStr (LineColPos 0 0 0)

runParser' :: String -> Parser a -> String -> IO a
runParser' inputName p s
    = do let (a,b) = execParser p s 
         if null b
          then return a
          else printf "Failed parsing '%s' :\n%s" inputName (showErrors s b) >> return a
    where showErrors :: String -> [Error LineColPos] -> String
          showErrors s errs = foldr (\e r -> "-> " ++ (pruneError s e) ++ r) "\n" errs
          pruneError :: String -> Error LineColPos -> String
          pruneError _ (DeletedAtEnd x    ) = printf "\nUnexpected '%s' at end." x
          pruneError s (Inserted x pos exp) = prettyError s exp pos ++ printf "\n\t Corrected with inserting: '%s'" x
          pruneError s (Deleted  x pos exp) = prettyError s exp pos ++ printf "\n\t Corrected with deleting: %s" x

          prettyError :: String -> [String] -> LineColPos -> String
          prettyError s exp p@(LineColPos line c abs) = printf "\nExpected %s at %s :\n%s\n%s\n%s\n"
                                                           (show_expecting p exp)
                                                           (show p)
                                                           aboveString
                                                           inputFrag
                                                           belowString
                             where
                                s' = map (\c -> if c=='\n' || c=='\r' || c=='\t' then ' ' else c) s
                                aboveString = replicate 30 ' ' ++ "v"
                                belowString = replicate 30 ' ' ++ "^"
                                inputFrag   = replicate (30 - c) ' ' ++ (take 71 $ drop (c - 30) s')


parseFileSistemasL :: FilePath -> IO [SistemaL]
parseFileSistemasL file 
    = do input <- readFile file
         res <- runParser' file pSistemasL input
         mbres <- mapM verificar res
         return $ catMaybes mbres
    where verificar sl = case (testSistemaL sl) of
                             Left errs -> do mapM_ putStrLn errs
                                             return Nothing
                             Right sl  -> return $ Just sl

pSistemasL :: Parser [SistemaL]
pSistemasL = pSpaces *> pList pSistemaL <* pSpaces

pSistemaL :: Parser SistemaL
pSistemaL = SistemaL <$> pNombre <* pSymbol "{" <*> pAlfabeto
                                 <* pSymbol ";" <*> pInicio
                                 <* pSymbol ";" <*> pProducciones
                                 <* pSymbol "}"

pAlfabeto :: Parser Alfabeto
pAlfabeto = pKeyword "alfabeto" *> pSymbol "=" *> pListaS1 (pSymbol ",") pSLSimbolo

pInicio :: Parser Inicio
pInicio = pKeyword "inicio" *> pSymbol "=" *> pListaS1 (pSymbol ",") pSLSimbolo

pProducciones :: Parser Producciones
pProducciones = pKeyword "producciones" *> pSymbol "=" *> pListaS1 (pSymbol ";") pProduccion

pProduccion :: Parser Produccion
pProduccion = (,) <$> pSLSimbolo <* pSymbol "->" <*> pListaS1 (pSymbol ",") pSLSimbolo

pListaS1 :: Parser String -> Parser a -> Parser [a]
pListaS1 sep simb = pSymbol "[" 
                        *> pList1Sep sep simb <*
                    pSymbol "]" 

pListaS :: Parser String -> Parser a -> Parser [a]
pListaS sep simb = pSymbol "[" 
                        *> pListSep sep simb <*
                   pSymbol "]" 

pNombre :: Parser String
pNombre = lexeme $ unwords <$> pList1Sep_ng pSpaces pSimpleWord

pSLSimbolo :: Parser Simbolo
pSLSimbolo = lexeme $ Simbolo <$> pMunch funchar
    where funchar c = not $ elem c "[]{},; \n\r\t"

-- auxiliar parsers
pSimpleWord :: Parser String
pSimpleWord = pList1 (pLetter <|> pDigit) <?> "Simple Word"

pKeyword :: String -> Parser String
pKeyword = lexeme . pToken