É chamado de compilação o processo de conversão do código escrito pelo programador para um arquivo binário que o computador consegue executar. Esse processo é realizado por um programa chamado compilador. Entre as diversas tarefas de um compilador, destaca-se a de identificar os possíveis erros sintáticos e semânticos.
Com base nessas informações, considere uma linguagem de programação em que a sintaxe de uma operação aritmética seja dada pela seguinte gramática livre de contexto:
S → var '=' E ';' | var '=' E; S
E → E '+' E | E '-' E | E '*' E | E '/' E | '(' E ')' | var
Inspirado nessa gramática, um profissional submete a seguinte sentença ao compilador dessa linguagem de programação:
\[a = a / (b - b);\]Com base na gramática da linguagem de programação e acerca do processo de análise sintática e semântica da sentença proposta pelo profissional, é correto afirmar que
A resposta correta é a letra B, pois o erro ocorre devido a uma divisão por zero na expressão b - b, e é necessária a avaliação do comportamento do código para que o erro seja detectado.
Em um compilador, um analisador sintático descendente preditivo pode ser implementado com o auxílio de uma tabela construída a partir de uma gramática livre de contexto. Essa tabela, chamada tabela LL(k), indica a regra de produção a ser aplicada olhando-se o k-ésimo próximo símbolo lido, chamado lookahead(k). Pro motivo de eficiência, normalmente busca-se utilizar k=1. Considere a gramática livre de contexto G = ({X, Y, Z}, {a, b, c, d, e}, P, X), em que P é composto pelas seguintes regras de produção (onde &
é a palavra vazia):
X -> aZbXY | c
Y -> dX | &
Z -> e
Considere, ainda, a seguinte tablea LL(1), construída a partir da gramática G, sendo $
o símbolo que representa o fim da cadeia. Essa tabela possui duas produções distintas na célula (Y, d), gerando, no analisador sintático, uma dúvida na escolha da regra de produção aplicada em determinados momentos da análise.
a | b | c | d | e | $ | |
X | X -> aZbXY | X -> c | ||||
Y | Y -> dX Y -> & |
Y -> & |
||||
Z | Z -> e |
Considerando que o processo de construção dessa tabela LL(1), a partir da gramátiac G, foi seguido corretamente, a existência de duas regras de produção distintas na célula (Y, d), neste caso específico, resulta
$
) nas regras de produção.&
) nas regras de produção.A resposta correta é a letra B, pois o indetermismo de saber qual regra utilizar, nesse caso, torna a gramática ambígua, ou seja, permite a criação de árvores de derivação diferentes para a mesma construção sintática.
Uma gramática livre de contexto (GLC) é um modelo computacional geralmente utilizado para definir linguagens de programação e, quando está de acordo com a Forma de Backus-Naur (BNF), permite que alguns operadores sejam utilizados no lado direito de suas producões, como o operador |
(pipe) que indica seleção, o operador []
que indica que o operonda em questão é opcional, e o operador *
que indica repetição de 0 (zero) ou mais vezes.
Projetar um compilador para uma determinada linguagem envolve, entre outras coisas, especificar quais são os símbolos válidos nesta linguagem, bem como quais são as regras sintáticas que a definem.
A linguagem de programação Java é uma linguagem com suporte à orientação a objetos que não permite herança múltipla e que premite que uma classe implemente múltiplas interfaces. A seguir, exibem-se trechos de código sintaticamente válidos na linguagem Java.
class A extends B { }
class F implements C { }
class J extends A implements C, D { }
No trecho 1, cria-se uma classe chamada A
que herda de uma classe chamada B
. No trecho 2, cria-se uma classe chamada F
que implementa uma interface chamada C
. No trecho 3, cria-se uma classe chamada J' que herda de uma classe chamada
A e implementa duas interfaces, chamadas
C e
D`.
Considere que:
<classdecl>
é um não-terminal cujo intuito é dar origem a declarações de classe;<classbody>
é um não-terminal cujo intuito é dar origem ao corpo de classes;A gramática que especifica uma linguagem que não permita herança múltipla e que implemente zero ou mais interfaces, como na linguagem Java, é:
<classdecl> "class" <id> ["extends"] <id> <classbody>
<classdecl> "class" <id> ("extends" <id>)* <classbody>
<classdecl> "class" <id> ["extends"] <id> ["implements" ("," | <id>)*] <classbody>
<classdecl> "class" <id> ["extends" <id>] ["implements" <id> ("," <id>)*] <classbody>
<classdecl> "class" <id> ["extends" <id>] "implements" <id> ("," <id>)* <classbody>
A resposta correta é a letra B. Essa pergunta requer atenção aos grupos que estão associados as regras de repetição e quais elementos são ou não opcionais.
Considere a implementação ede um compilador em que as etapas de análise léxica e sintática possam compartilhar o mesmo processador de forma concorrente. Considere, ainda, uma solução para o problema, cujo pseudocódigo é mostrado abaixo.
O analisador léxico lê lexemas e identifica os respectivos tokens do arquivo-fonte por meio da chamada ao procedimento Leia
. O analisador sintático verifica a sequência dos tokens por meio da chamada ao procedimento Case
. os dois processos compartilhas a constante N
e as variáveis buffer
, vez
e cont
.
constante N = 10;
inteiro buffer[N], vez = 0, cont = 0;
01 inteiro token, in = 0;
02 enquanto verdadeiro faça
03 Leia(token);
04 equanto cont = N - 1 aguarde;
05 equanto vez = 1 aguarde;
06 buffer[in] = token;
07 cont = cont + 1;
08 vez = 1;
09 in = (in + 1) mod N;
10 fim_enquanto
11 inteiro token, out = 0;
12 enquanto verdadeiro faça
13 enquanto cont = 0 aguarde;
14 enquanto vez = 0 aguarde;
16 token = buffer[out];
15 cont = cont - 1;
17 vez = 0;
18 out = (out + 1) mod N;
19 Case(token)
20 fim_enquanto
A partir da análise da solução, avalie as asserções a seguir e a relação proposta entre elas.
cont
e das linhas 4, 7, 13 e 16 causa erro de sincronismo entre os processos,PORQUE
cont
é responsável pelo controle de acesso à sção crítica do código.A respeito dessas asserções, assinale a opção correta.
A resposta correta é E, a variável cont controla a quantidade de elementos no array. A variável vez é que controla o sincronismo e acesso à seção crítica.
Utilizando o código intermediário de três endereçoes numerado, mostre o código gerado para a seginte instrução:
if a < 3 and b > 10 and a < b then verdadeiro() else falso() ;
100: t1 = a < 3
101: ifFalse t1 goto 108
102: t2 = b > 10
103: ifFalse t2 goto 108
104: t3 = a < b
105: ifFalse t3 goto 108
106: call 'verdadeiro'
107: goto 109
108: call 'falso'
109: ...