(→Tabela de operadores) |
(→Tabela de operadores) |
||
(32 intermediate revisions by the same user not shown) | |||
Line 14: | Line 14: | ||
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. | 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). | + | 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). |
== Expressões primitivas == | == Expressões primitivas == | ||
− | As expressões primitivas, ou representam directamente um valor (expressões literais), ou referem | + | As expressões primitivas, ou representam directamente um valor (expressões literais), ou referem um valor (identificadores). |
===Literais=== | ===Literais=== | ||
Line 39: | Line 39: | ||
Uma expressão composta é constituída por um operador, podendo ter argumentos. Cada argumento é representado por uma expressão. | 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, | + | 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, existem 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). | se executa o programa). | ||
Line 49: | Line 49: | ||
Exemplos: | Exemplos: | ||
− | * '''(add 1 2)''' -- representa a operação '''add''' com dois argumentos (expressões '''1''' e '''2'''), tendo o significado | + | * '''(add 1 2)''' -- representa a operação '''add''' com dois argumentos (expressões '''1''' e '''2'''), tendo o significado '''1+2''' |
− | * '''(mul (add 1 2) 3)''' -- representa a operação '''mul''' com dois argumentos (expressões '''(add 1 2)''' e '''3'''), tendo o significado | + | * '''(mul (add 1 2) 3)''' -- representa a operação '''mul''' com dois argumentos (expressões '''(add 1 2)''' e '''3'''), tendo o significado '''(1+2)*3''' |
* '''(eq id1 id2)''' -- representa a operação '''eq''' com dois argumentos (expressões designadas pelos identificadores '''id1''' e '''id2''') e testa se os valores das expressões referidas pelos identificadores '''id1''' e '''id2''' são iguais | * '''(eq id1 id2)''' -- representa a operação '''eq''' com dois argumentos (expressões designadas pelos identificadores '''id1''' e '''id2''') e testa se os valores das expressões referidas pelos identificadores '''id1''' e '''id2''' são iguais | ||
=== Tabela de operadores === | === Tabela de operadores === | ||
− | 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). A ordem de avaliação dos argumentos é sempre da esquerda para a direita, excepto onde indicado. | + | Os vários operadores e a correspondente semântica são apresentados na tabela seguinte. 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). A ordem de avaliação dos argumentos é sempre da esquerda para a direita, excepto onde indicado (note-se que este comportamento não é uniforme na linguagem C). |
{| | {| | ||
Line 101: | Line 101: | ||
! style="vertical-align: top; font-weight: normal;" | '''seq''' | ! style="vertical-align: top; font-weight: normal;" | '''seq''' | ||
! style="vertical-align: top; font-weight: normal;" | todos os tipos | ! style="vertical-align: top; font-weight: normal;" | todos os tipos | ||
− | ! style="vertical-align: top; font-weight: normal;" | As sub-expressões são avaliadas em sequência. O valor da expressão é o do seu último argumento. | + | ! style="vertical-align: top; font-weight: normal;" | As sub-expressões são avaliadas em sequência. O valor da expressão é o do seu último argumento. O valor de uma sequência vazia é '''0''' (zero). |
|- | |- | ||
! style="vertical-align: top; font-weight: normal;" | impressão (variádico) | ! style="vertical-align: top; font-weight: normal;" | impressão (variádico) | ||
Line 145: | Line 145: | ||
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. | 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. | ||
+ | |||
+ | Um programa pode não ter expressões (programa vazio). O valor de um programa vazio é '''0''' (zero). | ||
+ | |||
+ | Os identificadores definidos num programa são visíveis em todos os programas. | ||
= Funcionalidade do Interpretador = | = Funcionalidade do Interpretador = | ||
Line 158: | Line 162: | ||
Uma falha de interpretação causa o lançamento de uma excepção no analisador do interpretador. | Uma falha de interpretação causa o lançamento de uma excepção no analisador do interpretador. | ||
− | {{Suggestion|Note-se que não é necessário implementar de raiz o analisador de expressões e programas do interpretador. | + | {{Suggestion|Note-se que não é necessário implementar de raiz o analisador de expressões e programas do interpretador. Um analisador já parcialmente concretizado, '''pex.Parser''', está disponível. Este analisador é responsável por processar um programa ou uma expressão e realizar a conversão de texto para a estrutura de dados correspondente. Será apenas necessário indicar as entidades utilizadas para representar um programa ou uma expressão.}} |
== Armazenamento de programas == | == Armazenamento de programas == | ||
Line 166: | Line 170: | ||
== Interpretação de programas == | == Interpretação de programas == | ||
− | 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 | + | 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 avaliação corresponde simplesmente à apresentação de uma forma textual do programa (e das suas expressões). |
− | |||
− | |||
− | + | Uma outra avaliação possível é o cálculo dos valores das expressões do programa, correspondente à execução desse programa. | |
− | |||
− | |||
== Serialização == | == Serialização == | ||
Line 182: | Line 182: | ||
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 | + | As operações de pedido e apresentação de informação ao utilizador '''devem''' realizar-se através dos objectos ''form'' e ''display'', respectivamente, presentes em cada comando. 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 '''pt.tecnico.po.ui.DialogException''', são lançadas pelos comandos e tratadas por '''pt.tecnico.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 '''pt.tecnico.po.ui.DialogException''', são lançadas pelos comandos e tratadas por '''pt.tecnico.po.ui.Menu'''. Outras excepções não devem substituir as fornecidas nos casos descritos. | ||
Line 190: | Line 190: | ||
== Menu Principal == | == Menu Principal == | ||
− | As acções do menu | + | As acções do menu 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, o interpretador está vazio. |
=== Salvaguarda do Estado Actual do Interpretador === | === Salvaguarda do Estado Actual do Interpretador === | ||
Line 199: | Line 199: | ||
* '''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()'''. | * '''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 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. | * '''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. | ||
+ | |||
+ | As opções '''Criar''' e '''Abrir''' descartam eventuais alterações no interpretador ou no seu conteúdo. | ||
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. | ||
Line 209: | Line 211: | ||
* '''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. | * '''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). | + | * '''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). Note-se que o novo programa é interpretado: se a excepção '''pex.ParserException''' (do "core") for recebida, então a excepção '''pex.app.BadFileException''' (a desenvolver) deve ser lançada pelo comando. Neste caso, o programa não deve ser registado. |
− | * '''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. | + | * '''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()'''. Se o programa não existir, é comunicado o erro através de '''noSuchProgram()'''. 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'''.}} | {{CVSCode|Estes comandos já estão parcialmente implementados nas classes da ''package'' '''pex.app.main''' (disponível no CVS), respectivamente: '''NewProgram''', '''ReadProgram''', '''WriteProgram'''.}} | ||
+ | {{Aviso|É necessário definir a excepção '''pex.app.BadFileException''', devendo o seu construtor receber como argumento o nome do ficheiro que contém o programa a ler.}} | ||
=== Manipulação de Programa === | === Manipulação de Programa === | ||
− | Abre o menu de edição | + | Abre o menu de ediçã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()'''. |
− | {{CVSCode|Este comando já está parcialmente implementado na classe '''pex.app.main. | + | {{CVSCode|Este comando já está parcialmente implementado na classe '''pex.app.main.ManageProgram''' (disponível no CVS).}} |
== Menu de Manipulação de Programas == | == Menu de Manipulação de Programas == | ||
Line 232: | Line 235: | ||
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 | + | === Executar programa === |
Este comando executa o programa. | Este comando executa o programa. | ||
Line 238: | Line 241: | ||
=== Adicionar expressão ao programa === | === Adicionar expressão ao programa === | ||
− | Este comando permite adicionar uma nova expressão ao programa. Para tal, é | + | Este comando permite adicionar uma nova expressão ao programa. Para tal, é pedido o índice correspondente à posição de inserção (o primeiro índice é 0), 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 igual ao número de elementos do programa, a nova expressão é inserida no final do programa (após todas as outras). | ||
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. | 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. | ||
Line 246: | Line 251: | ||
=== Substituir expressão no programa === | === Substituir expressão no programa === | ||
− | Este comando permite substituir uma expressão existente no programa por uma nova expressão. Para tal, é | + | Este comando permite substituir uma expressão existente no programa por uma nova expressão. Para tal, é pedido o índice correspondente à posição de inserção (o primeiro índice é 0), 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. | 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. | ||
Line 254: | Line 259: | ||
=== Mostrar os identificadores presentes no programa === | === Mostrar os identificadores presentes no programa === | ||
− | Este comando permite listar todos os identificadores presentes no programa ( | + | Este comando permite listar todos os identificadores presentes no programa (apresentando um identificador por linha, por ordem alfabética), tanto nas expressões de definição, como nas que usam identificadores. |
=== Mostrar os identificadores do programa sem inicialização explícita === | === 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 ( | + | Este comando permite listar todos os identificadores presentes no programa que não são objecto de nenhuma incialização explícita (apresentando um identificador por linha, por ordem alfabética), i.e., não terem um valor previamente associado via operador '''set'''. |
= Leitura de Programas a Partir de Ficheiros Textuais = | = Leitura de Programas a Partir de Ficheiros Textuais = | ||
Line 267: | Line 272: | ||
Este programa é composto por três expressões, a última das quais é um ciclo. | Este programa é composto por três expressões, a última das quais é um ciclo. | ||
− | + | <lisp> | |
(seq (add 1 2) (sub 1 2) (mul (add 1 2) 3)) | (seq (add 1 2) (sub 1 2) (mul (add 1 2) 3)) | ||
(seq (add 1 2) (sub 1 2) (mul (add 1 2) 3) (div 2 5) "olá") | (seq (add 1 2) (sub 1 2) (mul (add 1 2) 3) (div 2 5) "olá") | ||
(seq (set ix 0) (while (lt ix 30) (seq (print "ix =" ix) (set ix (add ix 1))))) | (seq (set ix 0) (while (lt ix 30) (seq (print "ix =" ix) (set ix (add ix 1))))) | ||
+ | </lisp> | ||
== 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.--> | ||
− | + | <lisp> | |
(print "Introduza as dimensões do 1º lado do triângulo: ") | (print "Introduza as dimensões do 1º lado do triângulo: ") | ||
(set a (readi)) | (set a (readi)) | ||
Line 301: | Line 307: | ||
(print "Triângulo isósceles") | (print "Triângulo isósceles") | ||
(print "Triângulo escaleno"))))))))) | (print "Triângulo escaleno"))))))))) | ||
− | + | </lisp> | |
<!--A aplicação pode produzir ficheiros textuais com o conteúdo da sua memória, mas nunca os lê a partir de nenhum comando. | <!--A aplicação pode produzir ficheiros textuais com o conteúdo da sua memória, mas nunca os lê a partir de nenhum 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 um valor (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, existem 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 seguinte. 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). A ordem de avaliação dos argumentos é sempre da esquerda para a direita, excepto onde indicado (note-se que este comportamento não é uniforme na linguagem C).
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. O valor de uma sequência vazia é 0 (zero). |
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 literal | 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.
Um programa pode não ter expressões (programa vazio). O valor de um programa vazio é 0 (zero).
Os identificadores definidos num programa são visíveis em todos os programas.
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 avaliação corresponde simplesmente à apresentação de uma forma textual do programa (e das suas expressões).
Uma outra avaliaçã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 pedido e apresentação de informação ao utilizador devem realizar-se através dos objectos form e display, respectivamente, presentes em cada comando. 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 pt.tecnico.po.ui.DialogException, são lançadas pelos comandos e tratadas por pt.tecnico.po.ui.Menu. Outras excepções não devem substituir as fornecidas nos casos descritos.
As acções do menu 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, o interpretador está vazio.
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:
As opções Criar e Abrir descartam eventuais alterações no interpretador ou no seu conteúdo.
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 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 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, é pedido o índice correspondente à posição de inserção (o primeiro índice é 0), 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 igual ao número de elementos do programa, a nova expressão é inserida no final do programa (após todas as outras).
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, é pedido o índice correspondente à posição de inserção (o primeiro índice é 0), 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 (apresentando um identificador por linha, por ordem alfabética), 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 (apresentando um identificador por linha, por ordem alfabética), 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. <lisp>
(seq (add 1 2) (sub 1 2) (mul (add 1 2) 3)) (seq (add 1 2) (sub 1 2) (mul (add 1 2) 3) (div 2 5) "olá") (seq (set ix 0) (while (lt ix 30) (seq (print "ix =" ix) (set ix (add ix 1)))))
</lisp>
Este exemplo corresponde a um pequeno "programa" (triangulos.pex) para verificar se três segmentos de recta formam um triângulo. <lisp>
(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")))))))))
</lisp> 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.