Departamento de Ciência da Computação
- IME - USP
O objetivo desse terceiro exercício-programa é treinar a
Depois de fazer o EP3 e EP4 todos passarão a entender melhor o significados de expressões em C como:
if ((x = stackPop()) != '#') && (x != ')') || (x == '+') ) {...} a = b = c = 5; if (a < b < c) {...} /* nada a ver com semantica em Python */ while (!achou || continua) {...}
Este EP é uma adaptação do EP3 de MAC0122 em 2012 de autoria do professor Carlos Hitoshi Morimoto.
Neste EP você escreverá um programa que lê de um arquivo ou de um prompt expressões aritméticas em notação posfixa (= notação polonesa reversa) e calcula e imprime o resultado de cada uma das expressões.
Tudo que você fizer no EP3 será utilizado no EP4, Pitão II para a construção de uma calculadora de expressões infixas.
Cada expressão pode conter números e os seguintes símbolos correspondentes a 6 operadores relacionais, 8 operadores aritméticos e 3 operadores lógicos:
operação | símbolo |
---|---|
igual | == |
diferente | != |
maior ou igual | >= |
menor ou igual | <= |
maior | > |
menor | < |
exponenciação | ** |
resto de divisão | % |
multiplicação | * |
divisão | / |
divisão inteira | // |
adição | + |
subtração | - |
menos unário | _ |
e lógico | and |
ou lógico | or |
negação | not |
O programa pitao, quando invocado com a opção "-h" (help) mostra como deve ser utilizado. Nos exemplos de execução do programa, o texto em vermelho foi digitado pelo usuário.
prompt> ./pitao -h ./pitao: Uso prompt> ./pitao [-h] [-i | -s<nome-script> ] [-l] [-v] -h = mostra como usar o programa e abandona o programa -i = executa em modo interativo -s<nome-script> = calcula as expressoes no arquivo <nome-script> -l = mostra fila de itens lexicos -v = mostra fila de valores -p = mostra a pilha de execucao apos cada operacaoO programa deve ler uma sequência de linhas (strings). O maior comprimento de uma linha esta definido em main.c.
Copiado de main.cO programa supõe que cada linha contém uma expressão sintaticamente correta em notação posfixa. Exemplos de expressões que devem ser avaliadas pelo programa são:/* numero maximo de caracteres em um linha */ #define MAX_TAMANHO 128
2 2.5 + 25.6 4.2 3.1 * - 12 4.3 2.1 / - 3 * 15 - 3 4 2 ** ** 1 0 and 5 or 6 + 5 6 <= 6 3.3 %Para cada expressão posfixa o programa deve calcular e imprimir o seu valor.
O programa pitao pode ser usado no modo interativo (opção "-i", default) ou no modo script. No modo interativo, um prompt (>>>) é apresentado no início de cada linha para que a expressão seja digitada e lida. A seguir, as expressões em vermelho foram digitadas e os seus valores calculados.
prompt> ./pitao -i MAC0121 2019 - EP3 Pitao (Oct 9 2019, 12:54:06) [GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on Mac OS X >>> 2 2.5 + 4.500000 >>> 25.6 4.2 3.1 * - 12.580000 >>> 12 4.3 2.1 / - 3 * 15 - 14.857143 >>> 3 2 2 ** ** 81.000000 >>> 1 0 and 5 or 6 + 11.000000 >>> 5 6 <= 1.000000 >>> 6 3.3 % 2.700000Para executar pitao no modo script, o nome de um arquivo contendo as expressões deve ser fornecido ao programa através da opção "-s". Por exemplo, se o conteúdo de um arquivo entrada.txt é
# este e' um comentario 2 2.5 + 2 3 - 2.1 2 * 6.4 .8 / 3.1 not 2 3 ** 25.6 4.2 3.1 * - 12 4.3 2.1 / - 3 * 15 - 1 1 0 or and 5.1 _ # '_' e' o menos unario 6.2 2.7 % # vixe, como funciona esse tal %? 6.2 2.7 / 2 0 or 5 notPara calcular todas as expressões de um arquivo de nome entrada.txt no modo script, basta chamar o programa com a opção "-sentrada.txt", sem espaços entre o "-s" e o nome do arquivo.
prompt> ./pitao -sentrada.txt MAC0121 2019 - EP3 Pitao (Oct 9 2019, 12:54:06) [GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on Mac OS X Linha 0: # este e' um comentario Linha 1: 2 2.5 + 4.500000 Linha 2: 2 3 - -1.000000 Linha 3: 2.1 2 * 4.200000 Linha 4: 6.4 .8 / 8.000000 Linha 5: 3.1 not 0.000000 Linha 6: 2 3 ** 8.000000 Linha 7: Linha 8: 25.6 4.2 3.1 * - 12.580002 Linha 9: 12 4.3 2.1 / - 3 * 15 - 14.857143 Linha 10: 1 1 0 or and 1.000000 Linha 11: 5.1 _ # '_' e o menos unario -5.100000 Linha 12: 6.2 2.7 % # vixe, como funciona esse tal %? 0.800000 Linha 13: 6.2 2.7 / 2.296296 Linha 14: 2 0 or 1.000000 Linha 15: 5 not 0.000000
Após a leitura de cada linha, o programa faz uma análise léxica (http://en.wikipedia.org/wiki/Lexical_analysis) dos elementos da linha. Neste EP o analisador léxico recebe uma linha com uma expressão em notação posfixa e produz como saída uma lista (fila) de pares da forma
(item, categoria)Um item léxico (token) é uma cadeia de caracteres (string) que representa um elemento básico de uma linguagem. Os itens léxicos são classificados em unidades léxicas. Neste EP, essas unidades léxicas são indicadas pelo componente categoria do par. Ao executarmos o programa pitao com a opção "-l", o programa passa a apresentar a análise léxica dos itens que formam cada linha, ou seja, passa a exibir os pares da forma (item, categoria) correspondentes aos itens na linha.
prompt> ./pitao -i -l MAC0121 2019 - EP3 Pitao (Oct 9 2019, 12:54:06) [GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on Mac OS X >>> 12 4.3 2.1 / - 3 * 15 - ========================== Fila de itens lexicos item (categoria) . . . . . . . . . . . . . "12" (string representando int) "4.3" (string representando float) "2.1" (string representando float) "/" (operador aritmetico divisao) "-" (operador aritmetico subtracao) "3" (string representando int) "*" (operador aritmetico multiplicacao) "15" (string representando int) "-" (operador aritmetico subtracao) 14.857143 >>> 1 1 0 or and ========================== Fila de itens lexicos item (categoria) . . . . . . . . . . . . . "1" (string representando int) "1" (string representando int) "0" (string representando int) "or" (operador logico ou) "and" (operador logico e) 1.000000 >>>Por simplicidade, neste EP só teremos constantes numéricas e todas serão consideradas como sendo um float do Python (double do C). No programa, essas constantes serão armazenadas em variáveis do tipo double já que o float do Python corresponde ao double do C. Assim, no exemplo acima, o número 1 será armazenado em uma variável double (1.0).
As categorias a que um item pode pertencer neste EP3 estão definidas no arquivo categorias.h através da declaração de uma tabela.
Copiado de categorias.h/*-------------------------------------------------------------*/ /* temos 32 categorias de itens lexicos */ #define MAX_CATEGORIAS 32 enum listaCategorias { /* 4 operadores relacionais com 2 simbolos */ /* simbolo, constante */ OPER_IGUAL /* "==", 0 */ ,OPER_DIFERENTE /* "!=", 1 */ ,OPER_MAIOR_IGUAL /* ">=", 2 */ ,OPER_MENOR_IGUAL /* "<=", 3 */ /* 2 operadores aritmetico com 2 simbolos */ ,OPER_EXPONENCIACAO /* "**", 4 */ ,OPER_DIVISAO_INT /* "//", 5 */ /* 2 operadores relacionais com 1 simbolo */ ,OPER_MAIOR /* ">", 6 */ ,OPER_MENOR /* "<", 7 */ /* 6 operadores aritmeticos */ ,OPER_RESTO_DIVISAO /* "%", 8 */ ,OPER_MULTIPLICACAO /* "*", 9 */ ,OPER_DIVISAO /* "/", 10 */ ,OPER_ADICAO /* "+", 11 */ ,OPER_SUBTRACAO /* "-", 12 */ ,OPER_MENOS_UNARIO /* "_", 13 */ /* 3 operadores logicos */ ,OPER_LOGICO_AND /* "and", 14 */ ,OPER_LOGICO_OR /* "or", 15 */ ,OPER_LOGICO_NOT /* "not" , 16 */ /* operador de indexacao */ ,OPER_INDEXACAO /* "$" , 17 */ /*IGNORAR*/ /* atribuicao */ ,OPER_ATRIBUICAO /* "=" , 18 */ /* parenteses para expressoes */ ,ABRE_PARENTESES /* "(" , 19 */ /*EP4*/ ,FECHA_PARENTESES /* ")" , 20 */ /*EP4*/ /* colchetes para listas */ ,ABRE_COLCHETES /* "[" , 21 */ /*IGNORAR*/ ,FECHA_COLCHETES /* "]" , 22 */ /*IGNORAR*/ /* strings representando constantes */ ,BOOL_STR /* 23 */ ,FLOAT_STR /* 24 */ ,INT_STR /* 25 */ /* categorias das constantes */ ,STR /* 26 */ ,BOOL /* 27 */ ,FLOAT /* 28 */ ,INT /* 29 */ /* identificador */ ,ID /* nome do identificador, 30 */ /*EP4*/ /* categoria indefinida */ ,INDEFINIDA /* 31 */ }; typedef enum listaCategorias Categoria;
A análise léxica é feita pela função
Copiado de lexer.hque faz parte do esqueleto e está totalmente feita no módulo lexer.c. A descrição da função está no esqueleto.CelObjeto * crieFilaItens (char linha[]);
Ao final da análise léxica, temos uma fila de pares da forma
(item, categoria),onde item é um string representando o item léxico encontrado. Cada um desses itens (strings) será substituido por um valor e assim obteremos uma fila de pares da forma
(valor, categoria),Se a linha lida contém a expressão
3.14 5 +a análise léxica produzirá os pares
("3.14", FLOAT_STR) ("5", INT_STR) ("+", OPER_ADICAO)Na próxima fase, antes de partirmos para os cálculos das expressões, esses strings são substituidos por números da seguinte maneira:
(3.14, FLOAT) (5.0, FLOAT) (6, OPER_ADICAO)
Se o programa pitao é executado com a opção "-v", esses pares de valores e categorias passam a ser exibidos.
prompt> ./pitao -l -v MAC0121 2019 - EP3 Pitao (Oct 9 2019, 12:54:06) [GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on Mac OS X >>> 3.14 5 + ========================== Fila de itens lexicos item (categoria) . . . . . . . . . . . . . "3.14" (string representando float) "5" (string representando int) "+" (operador aritmetico adicao) ========================== Fila de valores valor (categoria) . . . . . . . . . . . . . . 3.140000 (valor float) 5.000000 (valor float) prec=6 (operador aritmetico adicao) 8.140000 >>>
A precedência entre os operadores não será utilizada neste EP, mas será fundamental no EP4. A precedência entre os operadores está na tabela a seguir que foi copiada da página Precedência entre operadores em C de Paulo Feofiloff. Aqui transparece um pouco da esquizofrenia do EP3 e EP4. A sintaxe e a semântica das operação serão herdadas do Python, já algumas coisas serão herdadas do C. Em C não existe o operador '//' (divisão inteira) e escrevemos '&&', '||' e '!' em vez de 'and', 'or' e 'not'.
_ not ^ | direita-para-esquerda | |
* / % // | esquerda-para-direita | |
+ - | esquerda-para-direita | |
< <= >= > | esquerda-para-direita | |
== != | esquerda-para-direita | |
and | esquerda-para-direita | |
or | esquerda-para-direita |
Valores inteiros associados a cada operador estão codificados na tabela
Copiado de eval.cdefinida no módulo eval.c e faz parte do esqueleto./*------------------------------------------------------------*/ /* Tabela com uma representacao da precedencia dos operadores * atraves de valores inteiros. * Quanto maior o valor, maior a precedencia. * * http://www.ime.usp.br/~pf/algoritmos/apend/precedence.html */ static const int precedencia[MAX_OPERADORES] = { [... código removido ...] };
No EP a função responsável pela substituição dos itens por valores, strings (FLOAT_STR e INT_STR) para floats (FLOAT) ou strings de operadores para ints (precedências) é
Copiado de eval.cque está no módulo eval.c e que você deverá escrever.void itensParaValores(CelObjeto *iniFilaItens);
Para calcular o valor de uma expressão, o programa simula uma pilha de execução. Se pitao for executado com a opção "-p", essa pilha de execução é exibida após cada valor ser empilhado ou desempilhado.
prompt> ./pitao -p MAC0121 2019 - EP3 Pitao (Oct 9 2019, 12:54:06) [GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on Mac OS X >>> 5 6 % 2 3 ** - ========================== Pilha de execucao valor . . . . . . . . . . . . . . . 5.000000 ========================== Pilha de execucao valor . . . . . . . . . . . . . . . 6.000000 5.000000 ========================== Pilha de execucao valor . . . . . . . . . . . . . . . 5.000000 ========================== Pilha de execucao valor . . . . . . . . . . . . . . . 2.000000 5.000000 ========================== Pilha de execucao valor . . . . . . . . . . . . . . . 3.000000 2.000000 5.000000 ========================== Pilha de execucao valor . . . . . . . . . . . . . . . 8.000000 5.000000 ========================== Pilha de execucao valor . . . . . . . . . . . . . . . -3.000000 -3.000000 >>>No EP o responsável por este cálculo será a função
Copiado de eval.cque está no módulo eval.c e que você deverá escrever.CelObjeto * eval (CelObjeto *iniPosfixa, Bool mostrePilhaExecucao);
A estrutura mais básica e fundamental neste EP é a CelObjeto.
Copiado de objetos.hO verdadeiro conteúdo de uma célula CelObjeto está no campo util e, dependendo do conteúdo do campo categoria, pode ser um double, um int ou um string./*--------------------------------------------------------------*/ /* CelObjeto e o tipo da celula utilizada para representar * todas as listas (pilhas ou filas) neste EP. * * Campo CATEGORIA * * Indica a categoria a qual o objeto pertence. Pode ser * um operador (OPER_*, um inteiro (INT), um double (FLOAT)... * Ver a lista de categorias em categorias.h. * * Campo VALOR * * Logo apos a analise lexica o campo valor contem um * string (valor.pStr) que representa o item lexico. * * Depois, esses strings serao substituidos pela funcao * itensParaValores (eval.c) por outros valores. * Esses outros valores dependem da categoria a que * o item pertence da seguinte maneira: * * - INT : valor(.vInt) contera um inteiro; * - FLOAT : valor(.vFloat) contera um double; * - OPER_*: valor(.vInt) contera um inteiro indicando a * a precedencia do operador. * * Campo PROX * * Ponteiro para a proxima celula. * */ typedef struct celObjeto CelObjeto; struct celObjeto { Categoria categoria; /* indica a categoria ao objeto */ Valor valor; /* representacao do 'valor' do Objeto */ CelObjeto *prox; /* ponteiro para a proxima celula */ };
Copiado de objetos.h/*---------------------------------------------------------------*/ /* Um CelObjeto contem um valor que pode ser: * * - um inteiro ('vInt' = valor int) ou * - um float ('vFloat' = valor double) ou * - um string ('pStr' = valor string = ponteiro para char) */ union valor { /* para precedencia de operadores (OPER_*)* */ int vInt; /* para tipo FLOAT: float em Python == double do C */ double vFloat; /* para categorias que utilizam strings INT_STR, FLOAT_STR e BOOL_STR */ char *pStr; }; typedef union valor Valor;
Neste EP as filas deverão ser implementadas por meio de lista encadeada com cabeça em que cada célula é do tipo CelObjeto. Essas filas também serão utilizadas pelo EP4.
Inicialmente, todas as células serão criadas (mallocSafe()) pela função crieFilaItens (lexer.c) que é responsável pela análise léxica e que já está completamente feita no esqueleto. Nas demais funções do programa essas células serão reutilizadas, sendo:
Hmmm. Dependendo de como for feita a implementação da função eval(), o programa não necessitará alocar célula alguma do tipo CelObjeto além das alocadas pela função CrieFileItens(), com exceção feita à célula cabeça da pilha de execução.
Para calcular o valor de uma expressão em notação posfixa a partir da fila iniPosfixa, a função eval() do seu programa deve utilizar uma pilha, a chamada pilha de execução.
A pilha de execução deve ser implementada por meio de uma lista encadeada com cabeça em que cada elemento é do tipo CelObjeto. A implementação desta pilha também será utilizada no EP4. (Hmmm, isto aqui está meio repetitivo...).
A sua função eval() deve examinar cada célula da fila iniPosfixa e:
O programa desenvolvido neste EP lê linhas com expressões na notação posfixa de um arquivo dado ou interativamente a partir do shell. As linhas são percorridas e, para cada linha, o programa:
Aqui estão listados os protótipos das funções que estão complemente feitas no esqueleto. O comportamento das funções está descrito nos comentários que precedem as funções nos arquivos do esqueleto.
No módulo lexer.c temos a função
Copiado de lexer.hCelObjeto * crieFilaItens (char linha[]);
No módulo util.c temos a função
Copiado de util.hvoid *mallocSafe (size_t nbytes);
No módulo main.c todas as funções estão completas.
Copiado de main.cstatic void mostreUso (char *nomePrograma); int main(int argc, char *argv[]);
Aqui estão listados os protótipos das funções que você deverá escrever. O comportamento das funções está descrito nos comentários que precedem as funções nos arquivos do esqueleto. Você sempre pode escrever mais funções auxiliares se achar conveniente.
No módulo objetos.c
void freeObjeto(CelObjeto *pObj); void freeListaObjetos(CelObjeto *iniLista); void mostreValor(CelObjeto *pValor); void mostreObjeto(CelObjeto *pObj, int tipoLista); void mostreListaObjetos(CelObjeto *iniLista, int tipoLista);
No módulo eval.c
void itensParaValores(CelObjeto *iniFila); CelObjeto * eval(CelObjeto *iniPosfixa, Bool mostrePilhaExecucao);
Além disso, você deverá definir no arquivo stack.h a interface de uma pilha e implementar as funções da interface no arquivo stack.c. A pilha deve ser implementada através de uma lista encadeada com cabeça.
Aqui temos uma visão geral dos arquivos que compõem o EP.
Este exercício-programa é formado por 8 arquivos 8-[.
O diretório com o esqueleto do EP contém, além desses 8 arquivos, um Makefile para
gerar o executável do pitao do EP.
Os arquivos no diretório com o esqueleto são
prompt/esqueleto> ls -l total 192 -rw-r--r--@ 1 cris staff 15434 Oct 9 12:50 00-esqueleto.tar -rw-r--r--@ 1 cris staff 777 Oct 9 10:45 Makefile -rw-r--r--@ 1 cris staff 3132 Oct 9 11:29 categorias.h -rw-r--r--@ 1 cris staff 7670 Oct 9 12:40 eval.c -rw-r--r--@ 1 cris staff 661 Oct 9 12:40 eval.h -rw-r--r--@ 1 cris staff 16326 Oct 9 11:10 lexer.c -rw-r--r--@ 1 cris staff 664 Oct 9 11:53 lexer.h -rw-r--r--@ 1 cris staff 5976 Oct 9 11:56 main.c -rw-r--r--@ 1 cris staff 9215 Oct 9 12:40 objetos.c -rw-r--r--@ 1 cris staff 3103 Oct 9 11:27 objetos.h -rw-r--r--@ 1 cris staff 1354 Oct 9 12:43 stack.c -rw-r--r--@ 1 cris staff 1431 Oct 9 12:39 stack.h -rw-r--r--@ 1 cris staff 902 Oct 9 12:42 util.c -rw-r--r--@ 1 cris staff 1033 Oct 9 12:38 util.hO arquivo 00-esqueleto.zip contém todos os arquivos .h, .c que formam o EP3 junto com o Makefile.
O arquivos que deverão ser manipulados são objetos.c, eval.c, stack.h e stack.c. No entanto, deverá ser entregue apenas um arquivo zip ou tar.gz, contendo todos os arquivos .h e .c e Makefile no diretório do seu EP3.
Os arquivos objetos.c e eval.c contêm os prótipos das funções que deverão ser feitas. Os comentários nos arquivos contêm a especificação do comportamento de cada função. Os arquivos stack.h e stack.c estão praticamente vazios aguardando pela sua interface predileta de uma pilha e a respectiva implementação através de uma lista encadeada com cabeça.
Para compilar o programa e gerar o executável basta, na linha de comando, digitar make (pelo menos no Linux e Mac OS). Faça isto assim que baixar o esqueleto e você verá as seguintes mensagens e um executável de nome pitao será gerado.
esqueleto> make > make gcc -Wall -ansi -O2 -pedantic -Wno-unused-result -c util.c gcc -Wall -ansi -O2 -pedantic -Wno-unused-result -c objetos.c objetos.c:50:14: warning: unused variable 'boolean' [-Wunused-variable] static char *boolean[2] = { "False", "True" }; ^ objetos.c:52:14: warning: unused variable 'nomeCategoria' [-Wunused-variable] static char *nomeCategoria[MAX_CATEGORIAS] = ^ 2 warnings generated. gcc -Wall -ansi -O2 -pedantic -Wno-unused-result -c stack.c stack.c:46:1: warning: ISO C requires a translation unit to contain at least one declaration [-Wempty-translation-unit] ^ 1 warning generated. gcc -Wall -ansi -O2 -pedantic -Wno-unused-result -c lexer.c gcc -Wall -ansi -O2 -pedantic -Wno-unused-result -c eval.c eval.c:62:18: warning: unused variable 'precedencia' [-Wunused-const-variable] static const int precedencia[MAX_OPERADORES] = ^ 1 warning generated. gcc -Wall -ansi -O2 -pedantic -Wno-unused-result -c main.c gcc util.o objetos.o stack.o lexer.o eval.o main.o -o pitao -lmNote que, apesar de no esqueleto o corpo das funções que deverão ser feitas estar vazio, o EP compila sem erros de sintaxe. No entanto, a compilação apresenta 3 avisos: os dois primeiros devido a tabelas não utilizadas (ainda) no arquivo objetos.c e o terceiro devido ao arquivo stack.c não conter código algum.
Como não há erros de sintaxe, um executável de nome pitao é gerado. Ao executar o programa e tentar, por exemplo, calcular o resultado de "4 + 2" verá
prompt> ./pitao MAC0121 2019 - EP3 Pitao (Oct 9 2019, 12:54:06) [GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on Mac OS X >>> 4 2 + AVISO: eval.c: Vixe! Ainda nao fiz a funcao itensParaValores. AVISO: eval.c: Vixe! Ainda nao fiz a funcao eval. AVISO: objetos.c: Vixe! Ainda nao fiz a funcao freeListaObjetos. >>>O programa não quebrará, ou seja, não veremos um segmentation fault, mas também não veremos grande coisa.
Agora, passamos à descrição dos arquivos.
Apenas os arquivos objetos.c, eval.c,
stach.h e stack.c
deverão ser manipulados. Não altere os nomes dos arquivos.
Nada deve ser feito ou alterado neste arquivo.
Contém declarações de constante que indicam as categorias dos itens léxicos e
a declaração do tipo Categoria.
Essas constantes e tipo são usados em todos os módulos.
/*-------------------------------------------------------------*/ /* * numero de operadores * 8 + 6 + 3 + 1 + 1 */ #define MAX_OPERADORES 19 /*-------------------------------------------------------------*/ /* temos 32 categorias de itens lexicos */ #define MAX_CATEGORIAS 32 enum listaCategorias { [...código removido ...] }; typedef enum listaCategorias Categoria;
prompt> make gcc -Wall -ansi -O2 -pedantic -Wno-unused-result -c objetos.c gcc -Wall -ansi -O2 -pedantic -Wno-unused-result -c stack.c gcc -Wall -ansi -O2 -pedantic -Wno-unused-result -c lexer.c gcc -Wall -ansi -O2 -pedantic -Wno-unused-result -c eval.c gcc -Wall -ansi -O2 -pedantic -Wno-unused-result -c main.c gcc util.o objetos.o stack.o lexer.o eval.o main.o -o pitao -lm