Departamento de Ciência da Computação - IME - USP

MAC0121 Algoritmos e Estruturas de Dados I

Segundo Semestre de 2019

Quarto Exercício Programa     

 

Pitão II

 


Objetivos

O objetivo desse quarto exercício-programa é treinar mais ainda a
  1. utilização de registros e structs,
  2. utilização de alocação dinâmica de memória,
  3. manipulação de listas encadeadas,
  4. manipulação de pilhas e
  5. manipulação de filas,
  6. aplicação de notação posfixa (polonesa reversa) para o cálculo de expressões e, em geral, compreensão da semântica de expressões em linguagens de programação como por exemplo Python e C.
  7. familiarização com o conceito de tabelas de símbolos.
De quebra todos utilizarão mais interfaces e verão um pouco mais do processo envolvido na interpretação/compilação de expressões.

Depois de fazer o EP3 e EP4 é esperado que todos passem a compreender melhor o significado (semântica) de expressões em C como:

    if ((x = stackPop()) != '#') ...

    a = b = c = 5;

    if (a < b < c) . . .

    while (!achou) ...
Em particular, a semântica de expressões como
     a < b < c < d
é diferente em Python e em C. Além disso, expressões como
     a = (b = 2 * (c = 2))
que não são aceitas pelo Python, são muito utilizadas em C. Nessas, e em outras situações, a semântica adotada neste EP é a da linguagem C, apesar da sintaxe (e semântica) utilizada para cada operador ser a do Python.

Este EP é uma adaptação feita pelo Professor José Coelho de Pina do EP4 de MAC0122 em 2012 de autoria do professor Carlos Hitoshi Morimoto

 


Introdução

Neste EP você implementará um interpretador de expressões em notação infixa que, como em um programa, também pode conter variáveis. De maneira semelhante ao EP3, você escreverá um programa que de um arquivo ou de um prompt uma sequência de expressões e calcula e imprime o resultado de cada uma. A diferença agora é que as expressões são mais semelhantes àquelas encontradas em linguagens de programação como C. As expressões serão em notação infixa e poderão conter variáveis. Como em um programa, variáveis poderão ser inicializadas por meio de atribuições e reutilizadas em expressões futuras.

Cada expressão pode conter variáveis, números, abre e fecha parênteses e os seguintes símbolos correspondentes a um operador de atribuição, 6 operadores relacionais, 8 operadores aritméticos e 3 operadores lógicos:

operação símbolo
atribuição =
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

 


Comportamento do programa

Como no EP3 o programa pitao poderá ser executado no modo interativo (opção "-i") ou no modo script (opção "-s").

O novo pitao deve ler uma sequência de linhas (strings), independentemente do modo selecionado.

O programa pitao, quando invocado com a opção "-h" (help) mostra como deve ser utilizado. Nos exemplos a seguir de execução do programa, texto em vermelho indica algo digitado pelo usuário.

prompt> ./pitao -h
./pitao: Uso 
prompt> ./pitao [-h] [-i | -s ] [-l] [-v] [-p] [-e] [-t]
    -h = mostra como usar o programa e abandona o programa
    -i = executa em modo (i)nterativo
    -s<nome-script> = calcula as expressões no arquivo <nome-script>
    -l = mostra fila de itens (l)exicos
    -v = mostra fila de (v)alores
    -p = mostra a (p)ilha de execucao apos cada operacao
    -e = mostra a (e)xpressao na notacao posfixa (EP4)
    -t = mostra a (t)abela de simbolos (EP4)
Note que o EP4 tem duas opções novas em relação ao EP3, as opções "-e" e "-t" que são descritas mais adiante.

Expressões infixas

O novo pitao recebe linhas contendo expressões em notação infixa e calcula e imprime o valor de cada expressão.
prompt> ./pitao -i
MAC0121 2019 - EP4
Pitao (Oct 25 2019, 12:09:28)
[GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on Mac OS X
>>> 2 **8 # 2 elevado a 8 
256
>>> 2 **_ 1 # 2 elevado a -1
0.5
>>> 2 + 3 *4              
14
>>> 2 ** 2 ** 3 # o mesmo que 2 elevado a (2 elevado a 3)              
256
Como agora as expressões estão na notação infixa, para alterar a ordem em que as operações são executadas devemos utilizar um par de abre e fecha parênteses.
>>> (2 + 3) * 4      
20
>>> (2 + 2) * 4 ** 0.5 # 4 elevado a 0.5 e 2
8
>>> ((2 + 2) * 4) ** 0.5 # raiz quadrada de 8
4
>>> (2 ** 2) ** 3 # o mesmo que 4 elevado a 3
64
Além disso, as expressões podem envolver variáveis que podem armazenar valores que podem ser reutilizados. Como em Python, para criar uma variável utilizamos o operador "=" de atribuição.
>>> a = 5
5
>>> a # o valor da expressao com uma variavel e o seu conteudo
5
>>> b = a + 1
6
>>> c = a + b
11
>>> c
11
>>> prova1 = 6
6
>>> prova2 = 7
7
>>> media = (prova1 + 2*prova2)/3        
6.66667
>>> media
6.66667
As expressões podem ainda conter operadores relacionais, aritméticos, lógicos e atribuições múltiplas, como em C.
>>> ep1 = 5
5
>>> ep2 = 2.5
2.5
>>> ep3 = 6
6
>>> mep = (ep1 + 2*ep2 + 3*ep3)/6
4.66667
>>> p1 = 5      
5
>>> p2 = 6
6
>>> mp = (p1 + 2* p2)/3
5.66667
>>> aprovado = mp >= 5 and mep >= 5
0
>>> i = j = k = 0
0
>>> i
0
>>> j
0
>>> k
0

Expressão posfixa

O programa transforma cada expressão em notação infixa lida em uma expressão em notação posfixa equivalente. O programa pitao pode imprimir essa expressão em notação posfixa, para isto basta ser executado com a opção "-e".

prompt> ./pitao -e
MAC0121 2019 - EP4
Pitao (Oct 25 2019, 12:09:28)
[GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on Mac OS X
>>> a = 1
Expressao posfixa:  a 1 =
1
>>> b = 2
Expressao posfixa:  b 2 =
2
>>> c = (a + 1) * b ** 0.5
Expressao posfixa:  c a 1 + b 0.5 ** * =
2.82843
>>> a = (b = c - 0.828443) * 2 + a 
Expressao posfixa:  a b c 0.828443 - = 2 * a + =
4.99997
>>> a = 1
Expressao posfixa:  a 1 =
1
>>> b = 2
Expressao posfixa:  b 2 =
2
>>> b = b + a 
Expressao posfixa:  b b a + =
3
>>> bool = b < 3
Expressao posfixa:  bool b 3 < =
0
>>> bool
Expressao posfixa:  bool
0

Tabela de símbolos

Uma tabela de símbolos (symbol table) é uma estrutura de dados utilizada por interpretadores e compiladores para manter informações sobre cada identificador tais como o seu tipo, a posição em que está armazenado e outras informações relevantes.

Em geral, uma tabela de símbolos é uma estrutura de dados que representa objetos ou itens, cada um dotado de uma chave (key). Essa estrutura deve ser capaz de realizar duas operações básicas: inserir (= insert = enter) um novo item e buscar (= search = lookup ) e retornar um item que tenha uma dada chave. Tabelas de símbolos também são chamadas de dicionários (dictionary) em analogia ao sistema bem conhecido de armazenar palavras (chaves) e seus significados (itens). Algumas linguagens de programação, como Python, possuem dicionários como um tipo nativo:

Python 3.7.4 (default, Jul  9 2019, 18:15:00)
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> estoque = { 'abacaxis': 430, 'peras': 238, 'bananas': 312, 'laranjas': 562} # chave:item
>>> type(estoque)
<type 'dict'>
>>> print(estoque['peras'])
238
>>> for fruta in estoque:
...     print("Temos", estoque[fruta], fruta)
...
('Temos', 562, 'laranjas')
('Temos', 238, 'peras')
('Temos', 312, 'bananas')
('Temos', 430, 'abacaxis')
A propósito, algumas linguagens também possuem pilhas e filas como tipo nativos. Por exemplo, vejam o que diz a página Data Structure.
Bem, esses comentários são apenas curiosidades. Voltemos ao que interessa...

O programa pitao deverá armazenar informações sobre as variáveis em uma tabela de símbolos. Se invocarmos pitao com a opção "-t", o valor de cada variável é mostrado após a interpretação de cada expressão.

prompt> ./pitao -t
MAC0121 2019 - EP4
Pitao (Oct 25 2019, 12:09:28)
[GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on Mac OS X
>>> (2 + 6) * 8 **_ 1
1

==========================
  Tabela de simbolos
  'nome': valor
. . . . . . . . . . . . . . .
  tabela vazia.

>>> soma = 0
0

==========================
  Tabela de simbolos
  'nome': valor
. . . . . . . . . . . . . . .
  'soma': 0

>>> prova1 = 7
7

==========================
  Tabela de simbolos
  'nome': valor
. . . . . . . . . . . . . . .
  'prova1': 7 
  'soma': 0 

>>> prova2 = 6.5
6.5

==========================
  Tabela de simbolos
  'nome': valor
. . . . . . . . . . . . . . .
  'prova2': 6.5 
  'prova1': 7 
  'soma': 0 

>>> prova3 = 4.5
4.5

==========================
  Tabela de simbolos
  'nome': valor
. . . . . . . . . . . . . . .
  'prova3': 4.5
  'prova2': 6.5
  'prova1': 7
  'soma': 0

>>> soma = prova1 + prova2 + prova3
18

==========================
  Tabela de simbolos
  'nome': valor
. . . . . . . . . . . . . . .
  'prova3': 4.5
  'prova2': 6.5
  'prova1': 7
  'soma': 18

>>> mediaProva = soma/3
6

==========================
  Tabela de simbolos
  'nome': valor
. . . . . . . . . . . . . . .
  'mediaProva': 6
  'prova3': 4.5
  'prova2': 6.5
  'prova1': 7
  'soma': 18

 


Visão geral do programa

Novos arquivos categorias.h, lexer.h, lexer.c... deverão ser copiados do esqueleto. Trechos de código que você escreveu para o EP3 deverão ser copiados para os arquivos do novo esqueleto. Alguns desses trechos de códigos deverão ser adaptados.

O programa pitao lê linhas com expressões de um arquivo dado ou interativamente a partir de um shell. Cada linha desse arquivo possui uma expressão infixa supostamente correta. A função main() do módulo main.c mostra um passo a passo no processo da interpretação de cada expressão. As linhas são percorridas e para cada linha pitao:
  1. imprime a linha e a expressão na linha (como no EP3);
  2. cria uma fila dos itens léxicos presentes na expressão: crieFilaItens() do mólulo lexer.c faz esse serviço (como no EP3).
  3. percorre a fila de itens substituindo no campo valor: Como no EP3, a função itensParaValores() do módulo eval.c é a responsável por essas substituições.
  4. percorre a nova fila resultante depois da substituição e produz uma nova fila que representa a expressão correspondente em notação posfixa: a função infixaParaPosfixa() do novo módulo posfixa.c faz essa tarefa.
  5. percorre a fila que representa uma expressão em notação posfixa contendo variáveis, números e operadores e interpreta a expressão: eval() do módulo eval.c do EP3 deverá ser adaptada para tratar o operador atribuição e variáveis;
  6. imprime o valor da expressão.
Além disso, ou no meio disso, pitao deverá administrar a tabela de símbolos. Isto deverá ser feito na função main() do módulo main.c (veja os comentário nas funções) e na função eval() do módulo eval.c.

 


Protótipos das funções fornecidas e parcialmente fornecidas

Aqui estão listados os protótipos das funções fornecidas ou parcialmente fornecidas e que você utilizará em algum dos trechos de código que deverá escrever. 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.h:
CelObjeto *
crieFilaItens (char linha[]);
que já foi adaptada para funcionar no EP4.

No módulo util.c temos as funções

Copiado de util.h:
void *
mallocSafe (size_t nbytes);

String 
getLine (FILE *infile);
que estão completamente escritas.

No módulo main.c temos as funções

Copiado de main.c:
static void 
mostreUso (char *nomePrograma);

int 
main(int argc, char *argv[]);
que deverão ser adaptadas para este EP4.

 


Protótipos das funções que você deve implementar.

 

Aqui estão listados os protótipos das funções que você deverá escrever. Algumas funções você já fez para o EP3 e deverão ser simplesmente copiadas para o EP4. Outras funções que você fez para o EP3 deverão ser adaptadas para o novo pitao. Já outras funções são totalmente novas e deverão ser completamente escritas para o EP4. O comportamento das funções está descrito nos comentários que precedem as funções nos arquivos do esqueleto ou ainda no meio da função, como no caso das funções main() e mostreUso() do módulo main.c.
Você (sempre!) pode escrever mais funções auxiliares.

Em um novo módulo, o posfixa.c temos a função

Copiado de posfixa.h:
CelObjeto *
infixaParaPosfixa (CelObjeto *iniInfixa);
que recebe uma expressão em notação infixa representada através de uma fila iniInfixa e retorna a expressão correspondente em notação posfixa. A expressão retornada também deve ser representada através de uma fila. Ambas as filas deverão ser implementadas através listas encadeadas com cabeça.

Em um outro novo módulo, o st.c, deverão ser implementadas todas as funções que manipulam a tabela de símbolos. A interface para a tabela de símbolos é composta pelas funções

Copiado de st.h:
void  
initST();

CelObjeto *
getValorST(char *nomeVar);

void  
setValorST(char *nomeVar, CelObjeto *pValor);

void  
showST(); 

void  
freeST();
Além dessas funções, o módulo st.c contém a função auxiliar
Copiado de st.c:
static CelST *static CelST *
endVarST(char *nomeVar);
que também deverá ser implementada. A tabela de símbolos deverá ser implementada através de uma lista encadeada com cabeça (vixe, mais uma. . .) em que cada célula é do tipo CelST. O tipo CelST é declarado em st.h

No módulo objetos.c

Copiado de objetos.h:
void  
freeObjeto(CelObjeto *pObj);

void  
freeListaObjetos(CelObjeto *iniLista);

void  
mostreValor(CelObjeto *pValor);

void  
mostreObjeto(CelObjeto *pObj, int tipoLista);

void  
mostreListaObjetos(CelObjeto *iniLista, int tipoLista);
As funções mostreObjeto() e mostreListaObjetos() deverão ser adaptada para imprimir a expressão posfixa. A função freeObjeto() deverá ser adaptada para dar free em uma CelObjeto que representa uma variável (categoria ID). Já as funções mostreValor(), freeObjeto() e freeListaObjetos() são as mesmas que foram escritas para o EP3.

No módulo eval.c

Copiado de eval.h:
void 
itensParaValores (CelObjeto *iniFila);

CelObjeto *
eval(CelObjeto *iniPosfixa, Bool mostrePilhaExecucao);
a função itensParaValores() talvez tenha que ser adaptada para tratar o operador de atribuição =. Nada precisa ser feito a respeito de abre e fecha parênteses. No entanto, a função eval() deverá ser adaptada para tratar o operador de atribuição e variáveis. O tratamento das variáveis será feito através das funções que estão na interface para a tabela de símbolos.

 


Tabela de símbolos, infixa para posfixa e interpretação de expressões

Estão descritos aqui alguns aspectos sobre as implementação. Talvez esta descrição seja meio que redundante se levamos em consideração o que já foi descrito e as especificações nos arquivos do esqueleto.

Tabela de símbolos

O programa utilizará uma tabela de símbolos para armazenar informações sobre as variáveis. Essa tabela será representada através de uma lista ligada com cabeça em que toda célula é do tipo CelST

Copiado de st.h:
/*----------------------------------------------------------*/
/* estrutura basica da tabela de simbolos */

typedef struct celST CelST;
struct celST 
{
    /* ponteiro para o nome da variavel */
    String nomeVar; 

    /* tipo da variavel (neste EP e' sempre FLOAT) */
    Categoria tipoVar;

    /* valor da variavel (neste EP e' sempre um double) */
    Valor valorVar;

    /* proxima celula da tabela de simbolos */
    CelST *proxVar; 
};
O campo nomeVar é um ponteiro para o string com o nome da variável. O campo tipoVar indica o tipo da variável, que nesse EP é sempre FLOAT. Neste EP, como as variaveis são do tipo FLOAT, um double, que representa o valor da variável, é armazenado no campo valorVar.

Infixa para posfixa

Cada linha do arquivo de entrada contém uma expressão infixa correta formada por números, variáveis e por operadores. Após a leitura de cada linha contendo uma expressão infixa, o programa pitao produz uma fila com os itens léxicos que formam a expressão na linha. Isto é feito pela função crieFilaItens() do módulo lexer.c. Em seguida, essa fila é tratada pela função itensParaValores() do módulo eval.c. Em seguida essa fila será convertida em uma fila que representa a expressão correspondente em notação posfixa. Essa conversão deve ser feita pela função

Copiado de posfixa.h:
CelObjeto *
infixaParaPosfixa (CelObjeto *iniInfixa);
do módulo posfixa.c. Essa função recebe a fila contendo a expressão infixa e retorna a fila com a expressão correspondente na notação posfixa. Esta função é uma adaptação da função de mesmo nome dessa página.

Ao longo de todo processo as filas são implementadas através de uma lista encadeadas com cabeça em que toda célula é do tipo CelObjeto, como as filas do EP3. Na fila com a expressão posfixa, cada célula armazena uma variável, um número, ou um operador. Suponha que p é um ponteiro para uma célula dessa fila.

  1. se a célula armazena uma variável, então p->categoria == ID (ID é uma constante definida em categorias.h) e p->valor.pStr é o ponteiro para o string com o nome da variável;
  2. se a célula armazena um número, então p->categoria == FLOAT (FLOAT é uma constante definida em categorias.h) e p->valor.vFloat contém o número;
  3. se a célula armazena um operador, então p->categoria == OPER_* (aqui, * significa um dos possíveis complementos na tabela enum categoria definida em categoria.h) e p->valor.vInt é um inteiro que indica a precedência desse operador como definida na tabela static const int precedencia[] definida no módulo eval.c.

Macros podem tornar mais conveniente e claro o acesso através de ponteiros aos campos de uma estrutura como CelObjeto. Para isso foram declaradas as macros a seguir.

Copiado de objetos.h:
/* nome de variaveis */
/* torna possivel escrevermos p->nomeID em vez de p->valor.pStr */
#define nomeID  valor.pStr

/* precedencia de operadores */ 
/* torna possivel escrevermos p->prec em vez de p->valor.pInt */
#define prec    valor.pInt

/* valores */
/* torna possivel escrevermos p->val em vez de p->valor.vFloat */
#define val     valor.pFloat

Interpretação da expressão posfixa

Para interpretar a expressão posfixa a partir da fila, o seu programa deve utilizar uma pilha. A pilha deve ser implementada por meio de uma lista encadeada com cabeça em que cada elemento é do tipo CelObjeto. Como no EP3, essa tarefa será feita pela função

Copiado de eval.h:
CelObjeto * eval(CelObjeto *iniPosfixa, Bool mostrePilhaExecucao);
que deverá ser adaptada para o EP4.

Na descrição a seguir, por valor de uma célula pCel entenda-se:

O seu programa deve examinar cada célula da fila com a expressão posfixa e:

Procedendo da forma acima, ao final, o valor da expressão se encontra no topo da pilha. É o endereço dessa célula no topo que deverá ser retornado pela função eval().

Para saber mais detalhes de como o programa realiza o cálculo da expressão em notação posfixa, consulte as notas de aula.

 


Precedência entre operadores

A tabela abaixo é baseada na tabela de precedência disponível aqui. Nessa tabela, os operadores estão em ordem decrescente de prioridade: na primeira linha os operadores executados em primeiro lugar e na última os operadores executados por último.  Assim, o operador '=' possui a menor precedência dentre os operadores da tabela. A coluna direta indica a convenção de associação para os operadores da linha.

  ( ) esquerda-para-esquerda
  _  not  ^ direita-para-esquerda
  *  /  %  // esquerda-para-direita
  +  - esquerda-para-direita
  <  <=  >=  > esquerda-para-direita
  ==  != esquerda-para-direita
  and esquerda-para-direita
  or esquerda-para-direita
  direita-para-esquerda

A tabela abaixo exemplifica a precedência dos operadores. Note, por exemplo, a associatividade da esquerda para direita entre operadores como '+' e '-' ou '*' e '/' e a associatividade da direita para esquerda dos operadores '_', 'not', '**' e '='.

expressão interpretação
2 + 3 + 4 (2 + 3) + 4
2 ** 3 ** 4 2 ** (3 ** 4)
A * B / C (A * B) / C
A = B + C / 3.2 / C     A = (B + ((C / 3.2) / C))
A = B = C = 3.2 A = (B = (C = 3.2))
A * _ B ** C - 2 ((A * (_ (B ** C)) - 2)

 


Descrição dos arquivos do EP

Aqui temos uma visão geral dos arquivos que compõem o EP.
Você deverá entregar apenas um arquivo zip ou tar.gz contendo todos os arquivos no diretório do seu EP4.

Este exercício programa é formado por 16 arquivos 8-[.
O diretório com esqueleto do EP contém, além desses 16 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 272
-rw-r--r--@ 1 cris  26744 Oct 25 11:27 00-esqueleto.zip
-rw-r--r--@ 1 cris    945 Oct 24 12:15 Makefile
-rw-r--r--@ 1 cris   3132 Oct 25 11:18 categorias.h
-rw-r--r--@ 1 cris   7971 Oct 24 12:15 eval.c
-rw-r--r--@ 1 cris    661 Oct 25 11:18 eval.h
-rw-r--r--@ 1 cris  16497 Oct 25 11:19 lexer.c
-rw-r--r--@ 1 cris    661 Oct 25 11:19 lexer.h
-rw-r--r--@ 1 cris   8591 Oct 25 11:19 main.c
-rw-r--r--@ 1 cris  10806 Oct 24 12:15 objetos.c
-rw-r--r--@ 1 cris   3697 Oct 25 11:19 objetos.h
-rw-r--r--@ 1 cris   2469 Oct 24 12:15 posfixa.c
-rw-r--r--@ 1 cris    384 Oct 24 12:15 posfixa.h
-rw-r--r--@ 1 cris   5846 Oct 24 12:15 st.c
-rw-r--r--@ 1 cris   1107 Oct 24 12:15 st.h
-rw-r--r--@ 1 cris   1594 Oct 24 12:15 stack.c
-rw-r--r--@ 1 cris   1494 Oct 25 11:20 stack.h
-rw-r--r--@ 1 cris   2833 Oct 25 11:20 util.c
-rw-r--r--@ 1 cris   1257 Oct 25 11:20 util.h

O arquivo 00-esqueleto.zip contém todos os arquivos .h, .c que formam o EP4 junto com o Makefile.

Os arquivos que deverão ser manipulados são main.c, objetos.c, eval.c, posfixa.c e st.c. Os demais arquivos não devem ser alterados.

Os arquivos no esqueleto contêm comentários nos protótipos das funções que deverão ser feitas, parcialmente feitas ou estão completas. Os comentários fornecem as especificação do comportamento de cada função.

Para compilar o programa e gerar o executável basta, na linha de comando, digitar make. Faça isto assim que baixar o esqueleto e você verá as seguintes mensagens e um executável de nome pitao será gerado.

prompt/esqueleto> make
gcc -Wall -ansi -g -pedantic -Wno-unused-result -c util.c
gcc -Wall -ansi -g -pedantic -Wno-unused-result -c objetos.c
objetos.c:57:14: warning: unused variable 'nomeCategoria' [-Wunused-variable]
static char *nomeCategoria[MAX_CATEGORIAS] =
             ^
1 warning generated.
gcc -Wall -ansi -g -pedantic -Wno-unused-result -c lexer.c
gcc -Wall -ansi -g -pedantic -Wno-unused-result -c stack.c
stack.c:51:1: warning: ISO C requires a translation unit to contain at least one declaration [-Wempty-translation-unit]
^
1 warning generated.
gcc -Wall -ansi -g -pedantic -Wno-unused-result -c posfixa.c
gcc -Wall -ansi -g -pedantic -Wno-unused-result -c st.c
st.c:49:15: warning: unused variable 'ini' [-Wunused-variable]
static CelST *ini;
              ^
st.c:194:1: warning: unused function 'endVarST' [-Wunused-function]
endVarST(char *nomeVar)
^
2 warnings generated.
gcc -Wall -ansi -g -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 -g -pedantic -Wno-unused-result -c main.c
gcc  util.o objetos.o lexer.o stack.o posfixa.o st.o eval.o main.o -o pitao -lm
Note que, apesar de no esqueleto o corpo das funções que deverão ser feitas estar vazio, o EP compila sem erros de sintaxe e com 5 avisos. Se você executar o programa imediatamente verá que ele não faz grandes coisas:
prompt/esqueleto> pitao
MAC0121 2019 - EP4
Pitao (Oct 25 2019, 12:09:28)
[GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on Mac OS X
>>> 1 + 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.
>>> a = 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.
>>> a
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.
>>>

Agora passamos à descrição dos arquivos.