Métodos de Programação I
LESI/LMCC
Universidade do Minho
Ano Lectivo de 2001/2002
Este trabalho deve ser realizado por grupos com um máximo de três alunos. O trabalho deve ser entregue até ao dia na Recepção do Departamento de Informática (ext. 4430) e nele deve constar a listagem do código desenvolvido assim como um pequeno relatório.
Neste trabalho pretende-se emular em HASKELL a abertura de uma «shell» de programação desse tipo. Uma tal «shell», uma vez invocada, deverá abrir um interpretador de comandos capaz de dar a ilusão de se estar a trabalhar em UNIX/LINUX. É claro que essa ilusão será tanto maior quanto mais extensa e detalhada for a emulação.
Tal como é habitual nos trabalhos desta disciplina, fornece-se de seguida um «kit» para arranque do projecto. O enunciado concluir-se-á com sugestões para valorização do trabalho a realizar.
(Se não está habituado a LATEX: em LINUX, faz parte da distribuição standard, é só experimentar; em Windows, que tal instalar o MiKTEX? Mas note que esse esforço, embora aconselhável, não é essencial à realização deste trabalho.)
Main> :l mpi0102t1.lhs Reading file "mpi0102t1.lhs": Hugs session for: /home/fln/share/hugs/lib/Prelude.hs mpi0102t1.lhs Main> shell $Note o novo «prompt» $. Para sair, escreva exit:
$ exit :: IO () Main>Observe assim o recurso à mónada IO. Volte a entrar e tente o bem conhecido comando cat:
Main> shell $ cat Comando desconhecido $Pois é, esse comando ainda não está previsto em mpi0102t1.lhs. E se tentar, por exemplo, wc, obterá
Main> shell $ wc Comando wc nao implementado $Aqui verifica-se que o comando está já previsto, mas ainda por implementar. Em boa verdade, mpi0102t1.lhs é uma concha de interpretação de comandos praticamente vazia. Apenas o comando head está (parcialmente!) implementado:
$ head -1 mpi0102t1.lhs \documentclass{article} $
O seu trabalho deverá consistir, precisamente, em encher shell de conteúdo apropriado. Vejamos então o que já está feito.
data Comando = Wc Opcoes [FilePath] | Head Opcoes [FilePath] | Tail Opcoes [FilePath] | Cut Opcoes [FilePath] | Desconhecido String type Opcoes = String
Poderá (e deverá) enriquecer esta sintaxe abstracta com mais construtores, por forma a enriquecer o «vocabulário» da sua shell.
readComando :: String -> Comando readComando l = let [(c,cs)] = lex l in case c of "wc" -> mkCom Wc cs "head" -> mkCom Head cs "tail" -> mkCom Tail cs "cut" -> mkCom Cut cs _ -> Desconhecido l mkCom :: (Opcoes -> [FilePath] -> Comando) -> String -> Comando mkCom c s = case (words s) of [] -> c "" [] (('-':op):r) -> c op r (h:t) -> c "" (h:t)
executa :: Comando -> IO () executa (Wc op fichs) = putStrLn "Comando wc nao implementado" executa (Head op fichs) = sequence_ (map (rHead (read op)) fichs) executa (Tail op fichs) = putStrLn "Comando tail nao implementado" executa (Cut op fichs) = putStrLn "Comando cut nao implementado" executa (Desconhecido _)= putStrLn "Comando desconhecido"
Note que apenas o comando head está (parcialmente) implementado:
rHead :: Int -> FilePath -> IO () rHead n f = do { s <- readFile f ; sequence_ (map putStrLn (take n (lines s))) }
De facto, o comando não faz qualquer análise de erros e o output, quando invocado com múltiplos file paths, não corresponde exactamente ao que um sistema UNIX produz.
shell = do { putStr prompt; c <- getLine ; if c == "exit" then stop else do { executa (readComando c) ; shell } } stop :: IO () stop = return () prompt = "$ "