(→Interpretação de expressões) |
|||
(79 intermediate revisions by the same user not shown) | |||
Line 2: | Line 2: | ||
{{PRJPOMandatory20162017}} | {{PRJPOMandatory20162017}} | ||
{{TOCright}} | {{TOCright}} | ||
− | + | | |
− | + | {{Aviso|Esta página é obsoleta. Ver [[Programação com Objectos/Projecto de Programação com Objectos/Enunciado do Projecto de 2016-2017|versão publicada]].}} | |
+ | | ||
O objectivo do projecto é criar uma aplicação que permite gerir um conjunto de programas e as expressões de que são compostos. | O objectivo do projecto é criar uma aplicação que permite gerir um conjunto de programas e as expressões de que são compostos. | ||
Line 24: | Line 25: | ||
As expressões literais são de dois tipos: inteiros e cadeias de caracteres. | As expressões literais são de dois tipos: inteiros e cadeias de caracteres. | ||
− | * | + | * Literais inteiros são números decimais não negativos, constituídos por sequências de 1 (um) ou mais dígitos de '''0''' a '''9'''. Exemplos: '''1''', '''23''' |
* As cadeias de caracteres são delimitadas por aspas ('''"''') e podem conter quaisquer caracteres. <!--É possível designar caracteres por sequências especiais (iniciadas por '''\'''), especialmente úteis quando não existe representação gráfica directa. As sequências especiais correspondem aos caracteres [[wp:ASCII|ASCII]] LF, CR e HT ('''\n''', '''\r''' e '''\t''', respectivamente), aspa ('''\"'''), barra ('''\\''').--> Exemplos: '''"hello, world!\n"''', '''"abcd"'''. | * As cadeias de caracteres são delimitadas por aspas ('''"''') e podem conter quaisquer caracteres. <!--É possível designar caracteres por sequências especiais (iniciadas por '''\'''), especialmente úteis quando não existe representação gráfica directa. As sequências especiais correspondem aos caracteres [[wp:ASCII|ASCII]] LF, CR e HT ('''\n''', '''\r''' e '''\t''', respectivamente), aspa ('''\"'''), barra ('''\\''').--> Exemplos: '''"hello, world!\n"''', '''"abcd"'''. | ||
Line 110: | Line 111: | ||
! style="vertical-align: top; font-weight: normal;" | leitura (sem argumentos) | ! style="vertical-align: top; font-weight: normal;" | leitura (sem argumentos) | ||
! style="vertical-align: top; font-weight: normal;" | '''readi''' '''reads''' | ! style="vertical-align: top; font-weight: normal;" | '''readi''' '''reads''' | ||
− | ! style="vertical-align: top; font-weight: normal;" | | + | ! style="vertical-align: top; font-weight: normal;" | - |
! style="vertical-align: top; font-weight: normal;" | Pedem a introdução (pelo utilizador) de valores com os tipos correspondentes. | ! style="vertical-align: top; font-weight: normal;" | Pedem a introdução (pelo utilizador) de valores com os tipos correspondentes. | ||
|- | |- | ||
Line 122: | Line 123: | ||
! style="vertical-align: top; font-weight: normal;" | condição (1º argumento) deve ser inteira | ! style="vertical-align: top; font-weight: normal;" | condição (1º argumento) deve ser inteira | ||
! style="vertical-align: top; font-weight: normal;" | Avalia o 1º argumento: se o valor lógico for falso, retorna-o; se o valor lógico for verdadeiro, avalia o 2º argumento, após o que reinicia o ciclo com nova avaliação do 1º argumento, etc. O valor da expressão é o valor da mais recente avaliação do seu primeiro argumento, ou seja, devolve sempre o valor correspondente à condição falsa ('''0''', zero). | ! style="vertical-align: top; font-weight: normal;" | Avalia o 1º argumento: se o valor lógico for falso, retorna-o; se o valor lógico for verdadeiro, avalia o 2º argumento, após o que reinicia o ciclo com nova avaliação do 1º argumento, etc. O valor da expressão é o valor da mais recente avaliação do seu primeiro argumento, ou seja, devolve sempre o valor correspondente à condição falsa ('''0''', zero). | ||
+ | |- | ||
+ | ! style="vertical-align: top; font-weight: normal;" | chamada (unário) | ||
+ | ! style="vertical-align: top; font-weight: normal;" | '''call''' | ||
+ | ! style="vertical-align: top; font-weight: normal;" | o nome do programa a chamar deve ser uma cadeia de caracteres | ||
+ | ! style="vertical-align: top; font-weight: normal;" | Executa o programa nomeado pelo argumento, devolvendo o valor da sua última expressão. <br>Exemplo: '''(call "nome-do-programa")''' | ||
|} | |} | ||
Line 132: | Line 138: | ||
Exemplo 2: sequência com 5 expressões (valor '''"olá"'''): | Exemplo 2: sequência com 5 expressões (valor '''"olá"'''): | ||
− | * '''(seq (add 1 2) (sub 1 2) (mul (add 1 2) 3) (div 2 | + | * '''(seq (add 1 2) (sub 1 2) (mul (add 1 2) 3) (div 2 5) "olá")''' |
Exemplo 3: sequência com 2 expressões (valor final '''0''') (note-se que esta sequência corresponde a um pequeno "programa" que apresenta o valor do identificador '''ix''' na saída): | Exemplo 3: sequência com 2 expressões (valor final '''0''') (note-se que esta sequência corresponde a um pequeno "programa" que apresenta o valor do identificador '''ix''' na saída): | ||
Line 139: | Line 145: | ||
== Estrutura de um programa == | == Estrutura de um programa == | ||
− | Um programa tem um nome e pode ser executado. A execução de um programa corresponde a avaliar todas as suas expressões sequencialmente, com início na primeira. | + | Um programa é formado por um conjunto de expressões, tem um nome e pode ser executado. A execução de um programa corresponde a avaliar todas as suas expressões sequencialmente, com início na primeira. |
= Funcionalidade do Interpretador = | = Funcionalidade do Interpretador = | ||
− | O interpretador permite interpretar (representar o texto como objectos) e avaliar expressões (calcular os seus valores). Possui também várias formas de preservar o seu estado. | + | O interpretador permite interpretar (representar o texto como objectos) e avaliar expressões (calcular os seus valores). Possui também várias formas de preservar o seu estado (não é possível manter várias versões do estado do interpretador em simultâneo). |
== Interpretação de expressões == | == Interpretação de expressões == | ||
Line 177: | Line 183: | ||
Descreve-se nesta secção a '''funcionalidade máxima''' da interface com o utilizador. Em geral, os comandos pedem toda a informação antes de proceder à sua validação (excepto onde indicado). Todos os menus têm automaticamente a opção '''Sair''' (fecha o menu). | Descreve-se nesta secção a '''funcionalidade máxima''' da interface com o utilizador. Em geral, os comandos pedem toda a informação antes de proceder à sua validação (excepto onde indicado). Todos os menus têm automaticamente a opção '''Sair''' (fecha o menu). | ||
− | As operações de leitura e escrita '''devem''' realizar-se através do objecto '''ist.po.ui.Dialog.IO'''. As mensagens são produzidas pelos métodos das [[Programação com Objectos/Projecto de Programação com Objectos/Material de Apoio ao Desenvolvimento|bibliotecas de suporte]] ('''po- | + | As operações de leitura e escrita '''devem''' realizar-se através do objecto '''ist.po.ui.Dialog.IO'''. As mensagens são produzidas pelos métodos das [[Programação com Objectos/Projecto de Programação com Objectos/Material de Apoio ao Desenvolvimento|bibliotecas de suporte]] ('''po-uuilib''' e '''pex-app'''). As mensagens não podem ser usadas no núcleo da aplicação ('''pex-core'''). Além disso, não podem ser definidas novas. Potenciais omissões devem ser esclarecidas antes de qualquer implementação. |
As excepções usadas na interacção, excepto se indicado, são subclasses de '''ist.po.ui.DialogException''', são lançadas pelos comandos e tratadas por '''ist.po.ui.Menu'''. Outras excepções não devem substituir as fornecidas nos casos descritos. | As excepções usadas na interacção, excepto se indicado, são subclasses de '''ist.po.ui.DialogException''', são lançadas pelos comandos e tratadas por '''ist.po.ui.Menu'''. Outras excepções não devem substituir as fornecidas nos casos descritos. | ||
− | Note-se que os comandos e menus a seguir descritos, assim como o programa principal, já estão parcialmente implementados nas classes das ''packages'' '''pex. | + | {{CVSCode|Note-se que os comandos e menus a seguir descritos, assim como o programa principal, já estão parcialmente implementados nas classes das ''packages'' '''pex.app''', '''pex.app.main''' e '''pex.app.evaluator'''. Estas classes são de uso obrigatório e estão disponíveis no [[Programação com Objectos/Projecto de Programação com Objectos/Repositório CVS|CVS]] (módulo '''pex-app''').}} |
== Menu Principal == | == Menu Principal == | ||
− | As acções do menu, listadas em '''pex. | + | As acções do menu, listadas em '''pex.app.main.MenuEntry''', permitem gerir a salvaguarda do estado da aplicação: [[#Salvaguarda do Estado Actual do Interpretador|Criar]], [[#Salvaguarda do Estado Actual do Interpretador|Abrir]], [[#Salvaguarda do Estado Actual do Interpretador|Guardar]], [[#Criação, Leitura e Escrita de Programas|Criar Programa]], [[#Criação, Leitura e Escrita de Programas|Ler Programa]], [[#Criação, Leitura e Escrita de Programas|Escrever Programa]] e [[#Manipulação de Programa|Manipulação de Programa]]. A classe '''pex.app.main.Message''' define os métodos para geração das mensagens de diálogo. Inicialmente, a aplicação tem uma memória vazia. |
− | === Salvaguarda do Estado Actual === | + | === Salvaguarda do Estado Actual do Interpretador === |
− | O conteúdo do | + | O conteúdo do interpretador (inclui todos os programas actualmente carregados pelo interpretador) pode ser guardado para posterior recuperação (via serialização Java: '''java.io.Serializable'''). Na leitura e escrita do estado da aplicação, devem ser tratadas as excepções associadas. A funcionalidade é a seguinte: |
− | * '''Criar''' -- Cria um novo | + | * '''Criar''' -- Cria um novo interpretador sem ficheiro associado. |
− | * '''Abrir''' -- Carrega um | + | * '''Abrir''' -- Carrega um interpretador anteriormente salvaguardado, ficando o interpretador carregado associado ao ficheiro nomeado: pede-se o nome do ficheiro a abrir ('''openFile()'''). Caso o ficheiro não exista, é apresentada a mensagem '''fileNotFound()'''. |
− | * '''Guardar''' -- Guarda o | + | * '''Guardar''' -- Guarda o estado actual do interpretador no ficheiro associado. Se não existir associação, pede-se o nome do ficheiro a utilizar, ficando a ele associado. Esta interacção realiza-se através do método '''newSaveAs()'''. Não é executada nenhuma acção se não existirem alterações desde a última salvaguarda. |
A opção '''Sair''' nunca guarda o estado da aplicação, mesmo que existam alterações. | A opção '''Sair''' nunca guarda o estado da aplicação, mesmo que existam alterações. | ||
− | Estes comandos já estão parcialmente implementados nas classes da ''package'' '''pex. | + | {{CVSCode|Estes comandos já estão parcialmente implementados nas classes da ''package'' '''pex.app.main''' (disponível no CVS), respectivamente: '''New''', '''Open''', '''Save'''.}} |
+ | |||
+ | === Criação, Leitura e Escrita de Programas === | ||
− | + | É possível criar novos programas (vazios), ler programas a partir de ficheiros textuais e escrever programas sob a forma de ficheiros textuais. As operações são as seguintes: | |
− | A | + | * '''Criar Programa''' -- Permite criar um programa vazio. É pedido o nome do programa através de '''requestProgramId()'''. A utilização de um nome previamente registado substitui o programa a ele associado por um programa vazio. |
+ | * '''Ler Programa''' -- Permite ler e interpretar o texto de um programa a partir de um ficheiro. É lido o ficheiro indicado pelo método '''programFileName()'''. A leitura de novo ficheiro com o mesmo nome substitui o programa anterior com esse nome (nome do ficheiro indicado). | ||
+ | * '''Escrever Programa''' -- Permite guardar um programa como um ficheiro de texto, passível de ser lido novamente pelo interpretador. O interpretador pede o identificador do programa '''requestProgramId()''' e o nome do ficheiro onde deve ser guardado o programa, através do método '''programFileName()'''. Escritas no mesmo ficheiro substituem o conteúdo anterior, não ficando associado ao interpretador. | ||
− | + | {{CVSCode|Estes comandos já estão parcialmente implementados nas classes da ''package'' '''pex.app.main''' (disponível no CVS), respectivamente: '''NewProgram''', '''ReadProgram''', '''WriteProgram'''.}} | |
− | === Manipulação de | + | === Manipulação de Programa === |
Abre o menu de edição (alteração) de um programa e do seu conteúdo. O interpretador pede o identificador do programa '''requestProgramId()'''. Se o programa não existir, é comunicado o erro através de '''noSuchProgram()'''. | Abre o menu de edição (alteração) de um programa e do seu conteúdo. O interpretador pede o identificador do programa '''requestProgramId()'''. Se o programa não existir, é comunicado o erro através de '''noSuchProgram()'''. | ||
− | Este comando já está parcialmente implementado na classe | + | Este comando já está parcialmente implementado na classe '''pex.app.main.EditProgram''' (disponível no CVS). |
== Menu de Manipulação de Programas == | == Menu de Manipulação de Programas == | ||
− | Este menu permite efectuar operações sobre um programa. A lista completa é a seguinte: [[#Listar programa|Listar programa]], [[#Executar|Executar | + | Este menu permite efectuar operações sobre um programa. A lista completa é a seguinte: [[#Listar programa|Listar programa]], [[#Executar|Executar]], [[#Adicionar expressão|Adicionar expressão]], [[#Substituir expressão|Substituir expressão]], [[#Mostrar os identificadores presentes no programa|Mostrar os identificadores presentes no programa]], [[#Mostrar os identificadores não inicializados do programa|Mostrar os identificadores não inicializados do programa]]. |
As etiquetas das opções deste menu estão definidas na classe '''pex.app.evaluator.Label'''. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe '''pex.app.evaluator.Message'''. | As etiquetas das opções deste menu estão definidas na classe '''pex.app.evaluator.Label'''. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe '''pex.app.evaluator.Message'''. | ||
+ | |||
+ | {{CVSCode|Estes comandos já estão parcialmente implementados nas classes da ''package'' '''pex.app.evaluator''' (disponível no CVS), respectivamente: '''ShowProgram''', '''RunProgram''', '''AddExpression''', '''ReplaceExpression''', '''ShowAllIdentifiers''', '''ShowUninitializedIdentifiers'''.}} | ||
=== Listar programa === | === Listar programa === | ||
Line 221: | Line 233: | ||
Este comando apresenta a lista de expressões do programa em formato textual. | Este comando apresenta a lista de expressões do programa em formato textual. | ||
− | + | === Executar Programa === | |
− | |||
− | === Executar === | ||
Este comando executa o programa. | Este comando executa o programa. | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
=== Adicionar expressão ao programa === | === Adicionar expressão ao programa === | ||
Line 239: | Line 241: | ||
Este comando permite adicionar uma nova expressão ao programa. Para tal, é pedida a posição de inserção, através de '''requestPosition()''', sendo a nova expressão aí inserida. A nova expressão é lida como resposta a '''requestExpression()'''. | Este comando permite adicionar uma nova expressão ao programa. Para tal, é pedida a posição de inserção, através de '''requestPosition()''', sendo a nova expressão aí inserida. A nova expressão é lida como resposta a '''requestExpression()'''. | ||
− | Se a indicação de posição for inválida, deve | + | Se a indicação de posição for inválida, o comando deve lançar a excepção '''pex.app.BadPositionException''' e o programa não é alterado. |
− | |||
− | |||
− | + | Note-se que a nova expressão é interpretada: se a excepção '''pex.ParserException''' (do "core") for recebida, então a excepção '''pex.app.BadExpressionException''' deve ser lançada pelo comando. Neste caso, o programa não deve ser alterado. | |
=== Substituir expressão no programa === | === Substituir expressão no programa === | ||
Line 249: | Line 249: | ||
Este comando permite substituir uma expressão existente no programa por uma nova expressão. Para tal, é pedida a posição de inserção, através de '''requestPosition()''', sendo a expressão aí existente substituída pela nova expressão. A nova expressão é lida como resposta a '''requestExpression()'''. | Este comando permite substituir uma expressão existente no programa por uma nova expressão. Para tal, é pedida a posição de inserção, através de '''requestPosition()''', sendo a expressão aí existente substituída pela nova expressão. A nova expressão é lida como resposta a '''requestExpression()'''. | ||
− | Se a indicação de posição for inválida, deve | + | Se a indicação de posição for inválida, o comando deve lançar a excepção '''pex.app.BadPositionException''' e o programa não é alterado. |
− | |||
− | |||
− | + | Note-se que a nova expressão é interpretada: se a excepção '''pex.ParserException''' (do "core") for recebida, então a excepção '''pex.app.BadExpressionException''' deve ser lançada pelo comando. Neste caso, o programa não deve ser alterado. | |
=== Mostrar os identificadores presentes no programa === | === Mostrar os identificadores presentes no programa === | ||
− | Este comando permite listar todos os identificadores presentes no programa, tanto nas expressões de definição, como nas que usam identificadores. | + | Este comando permite listar todos os identificadores presentes no programa (apresentado um identificador por linha), tanto nas expressões de definição, como nas que usam identificadores. |
− | + | === Mostrar os identificadores do programa sem inicialização explícita === | |
− | + | Este comando permite listar todos os identificadores presentes no programa que não são objecto de nenhuma incialização explícita (apresentado um identificador por linha), i.e., não terem um valor previamente associado via operador '''set'''. | |
− | + | = Leitura de Programas a Partir de Ficheiros Textuais = | |
− | |||
− | |||
− | |||
− | = Leitura de | ||
− | Além das opções de manipulação de ficheiros descritas no [[#Salvaguarda do Documento Actual|menu principal]], é possível iniciar a aplicação com um ficheiro de texto especificado pela propriedade Java '''[[#Execução dos Programas e Testes Automáticos|import]]'''. Este ficheiro contém | + | Além das opções de manipulação de ficheiros descritas no [[#Salvaguarda do Documento Actual|menu principal]], é possível iniciar a aplicação com um ficheiro de texto especificado pela propriedade Java '''[[#Execução dos Programas e Testes Automáticos|import]]'''. Este ficheiro contém um programa que é avaliado pelo interpretador. <!-- Se não existirem definições de variáveis, a avaliação não deixa traços na memória da calculadora. Apenas é lida a primeira expressão presente no ficheiro.--> O programa fica registado com o nome '''import'''. |
== Exemplo de ficheiro a importar == | == Exemplo de ficheiro a importar == | ||
− | + | Este programa é composto por três expressões, a última das quais é um ciclo. | |
− | + | (seq (add 1 2.3) (sub 1 2) (mul (add 1 2) 3)) | |
− | + | (seq (add 1 2.3) (sub 1 2) (mul (add 1 2) 3.2) (div 2 5) "olá") | |
− | + | (seq (set ix 0) (while (lt ix 30) (seq (print "ix =" ix) (set ix (add ix 1))))) | |
− | |||
== Exemplo de pequeno "programa" == | == Exemplo de pequeno "programa" == | ||
− | Este exemplo corresponde a um pequeno "programa" ('''triangulos.pex''') para verificar se três segmentos de recta formam um triângulo. Tal como no exemplo anterior, os identificadores '''a''', '''b''' e '''c''' ficam associados a valores usados durante a avaliação, embora a expressão do "programa" não seja memorizada. | + | Este exemplo corresponde a um pequeno "programa" ('''triangulos.pex''') para verificar se três segmentos de recta formam um triângulo. <!--Tal como no exemplo anterior, os identificadores '''a''', '''b''' e '''c''' ficam associados a valores usados durante a avaliação, embora a expressão do "programa" não seja memorizada.--> |
− | + | (print "Introduza as dimensões do 1º lado do triângulo: ") | |
− | + | (set a (readi)) | |
− | + | (print "Introduza as dimensões do 2º lado do triângulo: ") | |
− | + | (set b (readi)) | |
− | + | (print "Introduza as dimensões do 3º lado do triângulo: ") | |
− | + | (set c (readi)) | |
− | + | (if (lt a 1) | |
− | + | (print "As dimensões dos lados do triângulo devem ser positivas") | |
− | + | (if (lt b 1) | |
− | + | (print "As dimensões dos lados do triângulo devem ser positivas") | |
− | + | (if (lt c 1) | |
− | + | (print "As dimensões dos lados do triângulo devem ser positivas") | |
− | + | (if (le (add a b) c) | |
− | + | (print "Não é um triângulo") | |
− | + | (if (le (add a c) b) | |
− | + | (print "Não é um triângulo") | |
− | + | (if (le (add c b) a) | |
− | + | (print "Não é um triângulo") | |
− | + | (if (eq a b) | |
− | + | (if (eq b c) | |
− | + | (print "Triângulo equilátero") | |
− | + | (print "Triângulo isósceles")) | |
− | + | (if (eq b c) | |
− | + | (print "Triângulo isósceles") | |
− | + | (print "Triângulo escaleno"))))))))) | |
− | |||
− | A | + | <!--A aplicação pode produzir ficheiros textuais com o conteúdo da sua memória, mas nunca os lê a partir de nenhum comando. |
− | + | --> | |
− | Embora o interpretador de expressões | + | Embora o interpretador de expressões tenha de assinalar problemas relativos à interpretação de expressões mal especificadas, assume-se que não existem entradas mal-formadas nestes ficheiros (embora tenham de ser detectadas). |
= Considerações sobre Flexibilidade e Eficiência = | = Considerações sobre Flexibilidade e Eficiência = | ||
− | Devem ser possíveis extensões ou alterações de funcionalidade com impacto mínimo no código já produzido para a | + | Devem ser possíveis extensões ou alterações de funcionalidade com impacto mínimo no código já produzido para a aplicação. O objectivo é aumentar a flexibilidade da aplicação relativamente ao suporte de novas funções. Em particular, a solução encontrada para salvaguardar textualmente o conteúdo do documento deve ser suficientemente flexível de modo a permitir visualizar o conteúdo de um documento noutro formato (por exemplo, XML) sem que isso implique alterações no código ''core'' da aplicação. |
= Execução dos Programas e Testes Automáticos = | = Execução dos Programas e Testes Automáticos = | ||
− | Usando os ficheiros '''test.pex''', '''test.in''' e '''test.out''', é possível verificar automaticamente o resultado correcto do programa. Note-se que é necessária a definição apropriada da variável '''CLASSPATH''' (ou da opção equivalente '''-cp''' do comando '''java'''), para localizar as classes do programa, incluindo a que contém o método correspondente ao ponto de entrada da aplicação ('''pex. | + | Usando os ficheiros '''test.pex''', '''test.in''' e '''test.out''', é possível verificar automaticamente o resultado correcto do programa. Note-se que é necessária a definição apropriada da variável '''CLASSPATH''' (ou da opção equivalente '''-cp''' do comando '''java'''), para localizar as classes do programa, incluindo a que contém o método correspondente ao ponto de entrada da aplicação ('''pex.app.App.main'''). As propriedades são tratadas automaticamente pelo código de apoio. |
− | java -Dimport=test.pex -Din=test.in -Dout=test.outhyp pex. | + | java -Dimport=test.pex -Din=test.in -Dout=test.outhyp pex.app.App |
Assumindo que aqueles ficheiros estão no directório onde é dado o comando de execução, o programa produz o ficheiro de saída '''test.outhyp'''. Em caso de sucesso, os ficheiros das saídas esperada ('''test.out''') e obtida ('''test.outhyp''') devem ser iguais. A comparação pode ser feita com o comando: | Assumindo que aqueles ficheiros estão no directório onde é dado o comando de execução, o programa produz o ficheiro de saída '''test.outhyp'''. Em caso de sucesso, os ficheiros das saídas esperada ('''test.out''') e obtida ('''test.outhyp''') devem ser iguais. A comparação pode ser feita com o comando: |
AVISOS - Avaliação em Época Normal |
---|
Esclarecimento de dúvidas:
|
Requisitos para desenvolvimento, material de apoio e actualizações do enunciado (ver informação completa em Projecto de Programação com Objectos):
|
Processo de avaliação (ver informação completa em Avaliação do Projecto):
|
Material de Uso Obrigatório |
---|
As bibliotecas po-uuilib e o conteúdo inicial do CVS são de uso obrigatório: |
|
A máquina virtual, fornecida para desenvolvimento do projecto, já contém todo o material de apoio. |
Uso Obrigatório: Repositório CVS |
Apenas se consideram para avaliação os projectos existentes no repositório CVS oficial.
Trabalhos não presentes no repositório no final do prazo têm classificação 0 (zero) (não são aceites outras formas de entrega). Não são admitidas justificações para atrasos em sincronizações do repositório. A indisponibilidade temporária do repositório, desde que inferior a 24 horas, não justifica atrasos na submissão de um trabalho. |
O objectivo do projecto é criar uma aplicação que permite gerir um conjunto de programas e as expressões de que são compostos.
Neste texto, o tipo negrito indica um literal (i.e., é exactamente como apresentado); o símbolo indica um espaço; e o tipo itálico indica uma parte variável (i.e., uma descrição).
Uma expressão é uma representação algébrica de uma quantidade, ou seja, todas as expressões podem ser avaliadas para obteção de um valor.
Uma expressão pode ser descrita por uma linguagem cuja interpretação resulta directamente, ou no valor (tipicamente, aplicável apenas em situações muito simples), ou na representação dessa expressão como uma estrutura de dados (tal como neste projecto), sobre a qual pode ser então executado o processo de avaliação, para determinação do valor correspondente.
Neste documento utiliza-se uma linguagem simples e uniforme para descrever qualquer expressão, que pode ser categorizada como expressão primitiva (literais e identificadores) ou como expressão composta (operadores).
As expressões primitivas, ou representam directamente um valor (expressões literais), ou referem outra expressão (identificadores).
As expressões literais são de dois tipos: inteiros e cadeias de caracteres.
Um identificador permite referenciar um valor. A avaliação do identificador corresponde à obtenção do seu valor.
São iniciados por uma letra, seguindo-se 0 (zero) ou mais letras ou dígitos. O comprimento do nome é ilimitado e dois nomes são distintos se houver alteração de maiúscula para minúscula, ou vice-versa, de pelo menos um carácter.
Se for referenciado um identificador que não tenha sido definido, o valor devolvido é 0 (zero) e o identificador mantém-se indefinido.
Uma expressão composta é constituída por um operador, podendo ter argumentos. Cada argumento é representado por uma expressão.
O valor de uma expressão composta corresponde à avaliação do operador sobre os seus argumentos (no caso dos operadores com argumentos) ou simplesmente do operador (no caso dos operadores sem argumentos). O tipo de argumentos e de resultado depende do operador.Por exemplo, há operadores que só suportam argumentos do tipo inteiro. A passagem de tipos inapropriados para um operador é um erro (mas esta verificação apenas deve ser feita quando se executa o programa).
O formato de representação de uma expressão composta é como se segue:
( nome-do-operador argumento-1 ... argumento-N )
Alguns operadores têm um número fixo de argumentos, enquanto outros têm um número variável de argumentos (operadores variádicos).
Exemplos:
Os vários operadores e a correspondente semântica são apresentados na tabela. A maioria dos operadores segue a semântica da família da linguagem C, excepto onde explicitamente indicado. Tal como em C, os valores lógicos são 0 (zero) (valor falso), e diferente de zero (valor verdadeiro).
Designação | Operadores | Operandos | Semântica |
aritméticos (unários) | neg | inteiros | C |
---|---|---|---|
aritméticos (binários) | add sub mul div mod | inteiros | C |
comparação (binários) | lt le ge gt | inteiros | C |
igualdade (binários) | eq ne | inteiros | C |
lógicos (unários) | not | inteiros | C |
lógicos (binários) | and or | inteiros | C -- Para and, o 2º argumento só é avaliado se o 1º não for falso. Para or, o 2º argumento só é avaliado se o 1º não for verdadeiro. |
atribuição (binário) | set | todos os tipos | O valor da expressão (2º argumento) é associado ao identificador (1º argumento). O valor da expressão é o do seu 2º argumento. |
sequência (variádico) | seq | todos os tipos | As sub-expressões são avaliadas em sequência. O valor da expressão é o do seu último argumento. |
impressão (variádico) | todos os tipos | As sub-expressões são avaliadas em sequência e os valores correspondentes apresentados na saída. O valor da expressão é o do seu último argumento. | |
leitura (sem argumentos) | readi reads | - | Pedem a introdução (pelo utilizador) de valores com os tipos correspondentes. |
condicional (trenário) | if | condição (1º argumento) deve ser inteira | C -- Comporta-se de modo semelhante ao do operador trenário ?: (não exige concordância de tipos entre os 2º e 3º argumentos). Apenas é avaliado o argumento correspondente ao valor lógico indicado pela condição. |
ciclo (binário) | while | condição (1º argumento) deve ser inteira | Avalia o 1º argumento: se o valor lógico for falso, retorna-o; se o valor lógico for verdadeiro, avalia o 2º argumento, após o que reinicia o ciclo com nova avaliação do 1º argumento, etc. O valor da expressão é o valor da mais recente avaliação do seu primeiro argumento, ou seja, devolve sempre o valor correspondente à condição falsa (0, zero). |
chamada (unário) | call | o nome do programa a chamar deve ser uma cadeia de caracteres | Executa o programa nomeado pelo argumento, devolvendo o valor da sua última expressão. Exemplo: (call "nome-do-programa") |
Os exemplos seguintes apresentam casos de uso simples dos operadores acima.
Exemplo 1: sequência com 3 expressões (valor 9):
Exemplo 2: sequência com 5 expressões (valor "olá"):
Exemplo 3: sequência com 2 expressões (valor final 0) (note-se que esta sequência corresponde a um pequeno "programa" que apresenta o valor do identificador ix na saída):
Um programa é formado por um conjunto de expressões, tem um nome e pode ser executado. A execução de um programa corresponde a avaliar todas as suas expressões sequencialmente, com início na primeira.
O interpretador permite interpretar (representar o texto como objectos) e avaliar expressões (calcular os seus valores). Possui também várias formas de preservar o seu estado (não é possível manter várias versões do estado do interpretador em simultâneo).
As expressões, apresentadas ao interpretador em forma de texto, devem ser interpretadas antes de serem armazenadas. O texto original não é preservado. Note-se que o processo de interpretação (conversão do texto para estruturas/objectos) é diferente do de avaliação (conversão das estruturas/objectos para valores).
O texto a interpretar (expressões ou programas) pode ser providenciado via interface do próprio interpretador ou na forma de um ficheiro que contém um programa.
Uma falha de interpretação causa o lançamento de uma excepção no analisador do interpretador.
O interpretador permite associar nomes a programas, preservando-os. Note-se que isto não corresponde a guardar valores calculados por esses programas, mas a guardar as suas descrições, i.e., as resultantes da interpretação da forma textual correspondente. Se um nome já estiver em uso, a associação ao programa anterior é perdida.
A interpretação de programas e, consequentemente, das suas expressões, deve ser feita de forma flexível. Ou seja, deve ser possível -- sem alterar o código das expressões ou do interpretador -- definir novas formas de avaliação. Por omissão, a interpretação corresponde simplesmente à apresentação de uma forma textual do programa (e das suas expressões).
Uma outra interpretação possível é o cálculo dos valores das expressões do programa, correspondente à execução desse programa.
É possível reiniciar, guardar e recuperar o estado actual do interpretador, preservando todos os programas e correspondentes nomes.
Descreve-se nesta secção a funcionalidade máxima da interface com o utilizador. Em geral, os comandos pedem toda a informação antes de proceder à sua validação (excepto onde indicado). Todos os menus têm automaticamente a opção Sair (fecha o menu).
As operações de leitura e escrita devem realizar-se através do objecto ist.po.ui.Dialog.IO. As mensagens são produzidas pelos métodos das bibliotecas de suporte (po-uuilib e pex-app). As mensagens não podem ser usadas no núcleo da aplicação (pex-core). Além disso, não podem ser definidas novas. Potenciais omissões devem ser esclarecidas antes de qualquer implementação.
As excepções usadas na interacção, excepto se indicado, são subclasses de ist.po.ui.DialogException, são lançadas pelos comandos e tratadas por ist.po.ui.Menu. Outras excepções não devem substituir as fornecidas nos casos descritos.
As acções do menu, listadas em pex.app.main.MenuEntry, permitem gerir a salvaguarda do estado da aplicação: Criar, Abrir, Guardar, Criar Programa, Ler Programa, Escrever Programa e Manipulação de Programa. A classe pex.app.main.Message define os métodos para geração das mensagens de diálogo. Inicialmente, a aplicação tem uma memória vazia.
O conteúdo do interpretador (inclui todos os programas actualmente carregados pelo interpretador) pode ser guardado para posterior recuperação (via serialização Java: java.io.Serializable). Na leitura e escrita do estado da aplicação, devem ser tratadas as excepções associadas. A funcionalidade é a seguinte:
A opção Sair nunca guarda o estado da aplicação, mesmo que existam alterações.
É possível criar novos programas (vazios), ler programas a partir de ficheiros textuais e escrever programas sob a forma de ficheiros textuais. As operações são as seguintes:
Abre o menu de edição (alteração) de um programa e do seu conteúdo. O interpretador pede o identificador do programa requestProgramId(). Se o programa não existir, é comunicado o erro através de noSuchProgram().
Este comando já está parcialmente implementado na classe pex.app.main.EditProgram (disponível no CVS).
Este menu permite efectuar operações sobre um programa. A lista completa é a seguinte: Listar programa, Executar, Adicionar expressão, Substituir expressão, Mostrar os identificadores presentes no programa, Mostrar os identificadores não inicializados do programa.
As etiquetas das opções deste menu estão definidas na classe pex.app.evaluator.Label. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe pex.app.evaluator.Message.
Este comando apresenta a lista de expressões do programa em formato textual.
Este comando executa o programa.
Este comando permite adicionar uma nova expressão ao programa. Para tal, é pedida a posição de inserção, através de requestPosition(), sendo a nova expressão aí inserida. A nova expressão é lida como resposta a requestExpression().
Se a indicação de posição for inválida, o comando deve lançar a excepção pex.app.BadPositionException e o programa não é alterado.
Note-se que a nova expressão é interpretada: se a excepção pex.ParserException (do "core") for recebida, então a excepção pex.app.BadExpressionException deve ser lançada pelo comando. Neste caso, o programa não deve ser alterado.
Este comando permite substituir uma expressão existente no programa por uma nova expressão. Para tal, é pedida a posição de inserção, através de requestPosition(), sendo a expressão aí existente substituída pela nova expressão. A nova expressão é lida como resposta a requestExpression().
Se a indicação de posição for inválida, o comando deve lançar a excepção pex.app.BadPositionException e o programa não é alterado.
Note-se que a nova expressão é interpretada: se a excepção pex.ParserException (do "core") for recebida, então a excepção pex.app.BadExpressionException deve ser lançada pelo comando. Neste caso, o programa não deve ser alterado.
Este comando permite listar todos os identificadores presentes no programa (apresentado um identificador por linha), tanto nas expressões de definição, como nas que usam identificadores.
Este comando permite listar todos os identificadores presentes no programa que não são objecto de nenhuma incialização explícita (apresentado um identificador por linha), i.e., não terem um valor previamente associado via operador set.
Além das opções de manipulação de ficheiros descritas no menu principal, é possível iniciar a aplicação com um ficheiro de texto especificado pela propriedade Java import. Este ficheiro contém um programa que é avaliado pelo interpretador. O programa fica registado com o nome import.
Este programa é composto por três expressões, a última das quais é um ciclo.
(seq (add 1 2.3) (sub 1 2) (mul (add 1 2) 3)) (seq (add 1 2.3) (sub 1 2) (mul (add 1 2) 3.2) (div 2 5) "olá") (seq (set ix 0) (while (lt ix 30) (seq (print "ix =" ix) (set ix (add ix 1)))))
Este exemplo corresponde a um pequeno "programa" (triangulos.pex) para verificar se três segmentos de recta formam um triângulo.
(print "Introduza as dimensões do 1º lado do triângulo: ") (set a (readi)) (print "Introduza as dimensões do 2º lado do triângulo: ") (set b (readi)) (print "Introduza as dimensões do 3º lado do triângulo: ") (set c (readi)) (if (lt a 1) (print "As dimensões dos lados do triângulo devem ser positivas") (if (lt b 1) (print "As dimensões dos lados do triângulo devem ser positivas") (if (lt c 1) (print "As dimensões dos lados do triângulo devem ser positivas") (if (le (add a b) c) (print "Não é um triângulo") (if (le (add a c) b) (print "Não é um triângulo") (if (le (add c b) a) (print "Não é um triângulo") (if (eq a b) (if (eq b c) (print "Triângulo equilátero") (print "Triângulo isósceles")) (if (eq b c) (print "Triângulo isósceles") (print "Triângulo escaleno")))))))))
Embora o interpretador de expressões tenha de assinalar problemas relativos à interpretação de expressões mal especificadas, assume-se que não existem entradas mal-formadas nestes ficheiros (embora tenham de ser detectadas).
Devem ser possíveis extensões ou alterações de funcionalidade com impacto mínimo no código já produzido para a aplicação. O objectivo é aumentar a flexibilidade da aplicação relativamente ao suporte de novas funções. Em particular, a solução encontrada para salvaguardar textualmente o conteúdo do documento deve ser suficientemente flexível de modo a permitir visualizar o conteúdo de um documento noutro formato (por exemplo, XML) sem que isso implique alterações no código core da aplicação.
Usando os ficheiros test.pex, test.in e test.out, é possível verificar automaticamente o resultado correcto do programa. Note-se que é necessária a definição apropriada da variável CLASSPATH (ou da opção equivalente -cp do comando java), para localizar as classes do programa, incluindo a que contém o método correspondente ao ponto de entrada da aplicação (pex.app.App.main). As propriedades são tratadas automaticamente pelo código de apoio.
java -Dimport=test.pex -Din=test.in -Dout=test.outhyp pex.app.App
Assumindo que aqueles ficheiros estão no directório onde é dado o comando de execução, o programa produz o ficheiro de saída test.outhyp. Em caso de sucesso, os ficheiros das saídas esperada (test.out) e obtida (test.outhyp) devem ser iguais. A comparação pode ser feita com o comando:
diff -b test.out test.outhyp
Este comando não deve produzir qualquer resultado quando os ficheiros são iguais. Note-se, contudo, que este teste não garante o correcto funcionamento do código desenvolvido, apenas verificando alguns aspectos da sua funcionalidade.