550 likes | 677 Views
CES-41 COMPILADORES Aulas Práticas - 2013. Capítulo V Interpretação do Código Intermediário. Código intermediário para linguagens sem subprogramas :. Interpretação: Colocar um ponteiro na 1ª quádrupla executável
E N D
CES-41 COMPILADORESAulas Práticas - 2013 Capítulo V Interpretação do Código Intermediário
Código intermediário para linguagens sem subprogramas: Interpretação: Colocar um ponteiro na 1ª quádrupla executável Executar cada quádrupla, sequencialmente, até encontrar aquela de operador OPEXIT As quádruplas de desvio alteram a sequência quad
Local para valores das variáveis (temporárias ou não): Na tabela de símbolos: Deve-se acrescentar os seguintes campos emcelsimb: dims cadeia tid tvar inic ref array ndims - - - - - - - - - - - - - - - - - - - - - valint valfloat valchar vallogic Poderia ser usada union int *valint; float *valfloat; char *valchar, *vallogic; Espaço para estes ponteiros serão alocados na execução da quádrupla OPENMOD
Exemplo: sejam as declarações int x; real y; int A[4,3]; real B[5]; dims dims cadeia tid tvar inic ref array ndims - - - - - - - - - - - - - - - - - - - valint valfloat valchar vallogic cadeia tid tvar inic ref array ndims - - - - - - - - - - - - - - - - - - - valint valfloat valchar vallogic A v i s 2 # 4 3 ? ? ? B v r s 1 # 5 ? ? ? Alocações na execução de OPENMOD dims cadeia tid tvar inic ref array ndims - - - - - - - - - - - - - - - - - - valint valfloat valchar vallogic x v i n ? ? ? dims cadeia tid tvar inic ref array ndims - - - - - - - - - - - - - - - - - - valint valfloat valchar vallogic y v r n ? ? ?
Programa 5.1: Esqueleto do interpretador para a gramática das aulas de lab Arquivos: pret012013.y, pret012013.l e pret012013.dat A seguir, o estado inicial da função principal do interpretador, no final do arquivo pret012013.y A ser completada durante a aula e durante a execução do projeto
voidInterpCodIntermed () { quadruplaquad, quadprox; char encerra; printf ("\n\nINTERPRETADOR:\n"); encerra = FALSE; quad = codintermed->prox->listquad->prox; while (! encerra) { printf ("\n%4d) %s", quad->num, nomeoperquad[quad->oper]); quadprox = quad->prox; switch (quad->oper) { case OPEXIT: encerra = TRUE; break; } if (! encerra) quad = quadprox; } printf ("\n"); } quad
Definição do operador de quádrupla OPEXIT: #define OPEXIT 24 Para guardar valores das variáveis, cada célula da tabela de símbolos terá os seguintes campos: int *valint; float *valfloat; char *valchar, *vallogic; Espaço para estes ponteiros serão alocados na execução da quádrupla OPENMOD
Protótipos de algumas funções para o interpretador (outras deverão ser construídas na elaboração do projeto): voidInterpCodIntermed (void); voidAlocaVariaveis (void); voidExecQuadWrite (quadrupla); voidExecQuadMais (quadrupla); voidExecQuadLT (quadrupla); void ExecQuadAtrib (quadrupla); void ExecQuadRead (quadrupla);
Na produção do não-terminal Prog: Prog : {- - - - -} PROGRAMA ID PVIRG {- - - - -} DeclsCmdComp { GeraQuadrupla (OPEXIT, opndidle, opndidle, opndidle); VerificaInicRef (); ImprimeTabSimb (); ImprimeQuadruplas (); InterpCodIntermed (); } ; Rodar flex, yacc, gcc e executável A seguir, o arquivo de dados
programa teste; var int a, b, c, i, j; real k; logic b1; int A[5,4]; { escrever ("Valor 1: ", 14, "; Valor 2: ", 15.2, "; Valor 3: ", verdade); escrever ("Valor de 3+4: ", 3+4); a := 1; b := 2; i := 3; j := 4; c := a + b + i + j + 20; k := 12.6; b1 := verdade; escrever ("c = ", c, "; k = ", k, "; b1 = ", b1, ";");
i := 4; j := 5; b1 := verdade; se (b1) {i := j + 6;} senao {i := i + 15;} escrever ("i = ", i); i := 4; j := 5; b1 := verdade; enquanto (b1) {i := i + j; b1 := falso;} escrever ("i = ", i); i := 3; k := 20.3; escrever ("i = ", i, "; k = ", k); /* enquanto (i < k) { i := i + 4; escrever ("i = ", i, "; k = ", k); } */ escrever ("Valor de i+k: ", i+k); }
Exercício 5.1: Execução da quádrupla OPENMOD No switch da função InterpCodIntermed: case OPENMOD: AlocaVariaveis (); break; Função AlocaVariáveis (já tem protótipo): Percorre todas as classes da TabSimb Para cada classe, visita todas as células Para cada célula de tipo IDVAR, aloca espaço para o valor da variável correspondente É preciso examinar o tipo e a dimensão da variável
Exemplo: sejam as declarações int x; real A[5,4]; dims cadeia tid tvar inic ref array ndims - - - - - - - - - - - - - - - - - - - valint valfloat valchar vallogic A v r s 3 # 5 4 ? ? ? dims - - - - - - - - - - - - - - - - - - - valint valfloat valchar vallogic cadeia tid tvar inic ref array ndims x v i n ? ? ? Apenas um elemento inteiro alocado para x 5*4 = 20 elementos reais alocados para A
voidAlocaVariaveis () { simbolo s; intnelemaloc, i, j; printf ("\n\t\tAlocando as variaveis:"); for (i = 0; i < NCLASSHASH; i++) if (tabsimb[i]) { for (s = tabsimb[i]; s != NULL; s = s->prox){ if (s->tid == IDVAR) { nelemaloc = 1; if (s->array) for (j = 1; j <= s->ndims; j++) nelemaloc *= s->dims[j]; switch (s->tvar) { case INTEGER: s->valint = malloc (nelemaloc * sizeof (int)); break; case FLOAT: s->valfloat = malloc (nelemaloc * sizeof (float)); break; case CHAR: s->valchar = malloc (nelemaloc * sizeof (char)); break; case LOGICAL: s->vallogic = malloc (nelemaloc * sizeof (char)); break; } printf ("\n\t\t\t%s: %d elemento(s) alocado(s) ", s->cadeia, nelemaloc); } } } } nelemaloc: número de elementos alocados para cada variável dims Executar e verificar a alocação das variáveis na execução de OPENMOD cadeia --------- ndims - - - - - - - - - - - - - - - - - - - valint valfloat valchar vallogic x ------- ? ? ? s
Exercício 5.2: Execução da quádrupla PARAM Exemplo: seja o comando escrever ("Valor de a+b:", a+b, "Valor de c:", c); Suas quádruplas: 1) PARAM, (CADEIA, Valor de a+b:), (IDLE), (IDLE) 2) MAIS, (VAR, a), (VAR, b), (VAR, ##1) 3) PARAM, (VAR, ##1), (IDLE), (IDLE) 4) PARAM, (CADEIA, Valor de c:), (IDLE), (IDLE) 5) PARAM, (VAR, c), (IDLE), (IDLE) 6) WRITE, (INT, 4), (IDLE), (IDLE)
1) PARAM, (CADEIA, Valor de a+b:), (IDLE), (IDLE) 2) MAIS, (VAR, a), (VAR, b), (VAR, ##1) 3) PARAM, (VAR, ##1), (IDLE), (IDLE) 4) PARAM, (CADEIA, Valor de c:), (IDLE), (IDLE) 5) PARAM, (VAR, c), (IDLE), (IDLE) 6) WRITE, (INT, 4), (IDLE), (IDLE) VAR, c CADEIA, Valor de c: VAR, ##1 CADEIA, Valor de a+b: pilhaoperando (global) Preenchida pelas 4 quádruplas PARAM pilhaopndaux A ser preenchida pela quádrupla WRITE
Execução da quádrupla: OPWRITE, (INT, 4), (IDLE), (IDLE) 1) Desempilhar 4 operandos de pilhaoperando, empilhando-os em pilhaopndaux VAR, c CADEIA, Valor de c: VAR, ##1 CADEIA, Valor de a+b: pilhaoperando (global) Preenchida pelas 4 quádruplas PARAM pilhaopndaux A ser preenchida pela quádrupla WRITE
Execução da quádrupla: OPWRITE, (INT, 4), (IDLE), (IDLE) 2) Desempilhar 4 operandos de pilhaopndaux, imprimindo seus atributos CADEIA, Valor de a+b: VAR, ##1 CADEIA, Valor de c: VAR, c pilhaoperando (global) Esvaziada pela quádrupla WRITE pilhaopndaux Preenchida pela quádrupla WRITE
Declarações para pilhas de operandos (já no arquivo pret012013.y): typedefstructnohopndnohopnd; structnohopnd { operando opnd; nohopnd *prox; }; typedefnohopnd *pilhaoperando; pilhaoperandopilhaopnd, pilhaopndaux;
Funções para manipular pilhas de operandos (já têm protótipos): voidEmpilharOpnd (operando x, pilhaoperando *P) { nohopnd *temp; temp = *P; *P = (nohopnd *) malloc (sizeof (nohopnd)); (*P)->opnd = x; (*P)->prox = temp; } voidDesempilharOpnd (pilhaoperando *P) { nohopnd *temp; if (! VaziaOpnd (*P)) { temp = *P; *P = (*P)->prox; free (temp); } elseprintf ("\n\tDelecao em pilha vazia\n"); }
operando TopoOpnd (pilhaoperando P) { if (! VaziaOpnd (P)) return P->opnd; elseprintf ("\n\tTopo de pilha vazia\n"); } voidInicPilhaOpnd (pilhaoperando *P) { *P = NULL; } charVaziaOpnd (pilhaoperando P) { if (P == NULL) return 1; elsereturn 0; }
Programação para executar quádruplas PARAM No início da função InterpCodIntermed: printf ("\n\nINTERPRETADOR:\n"); InicPilhaOpnd (&pilhaopnd); encerra = FALSO; quad = codintermed->listquad->prox; No switch da função InterpCodIntermed: case PARAM: EmpilharOpnd (quad->opnd1, &pilhaopnd); break;
Exercício 5.3: Execução da quádrupla OPWRITE Exemplo: WRITE, (INT, 4), (IDLE), (IDLE) A execução compreende os passos: Inicializar a pilha auxiliar de operandos Transportar os operandos da pilha oficial para a auxiliar Desempilhar cada operando da pilha auxiliar, imprimindo seu atributo
Escrita dos valores dos operandos: O problema é encontrar o paradeiro do valor a ser impresso Depende do tipo do operando, que pode ser: Uma variável Uma constante inteira, real, caractere, lógica ou cadeia de caracteres
Se for uma variável, ela pode ser inteira, real, caractere ou lógica: O valor do operando fica então guardado na TabSimb dims cadeia tid tvar inic ref array ndims - - - - - - - - - - - - - - - - - - - - valint valfloat valchar vallogic A v r s 3 # 5 4 ? ? ? dims cadeia tid tvar inic ref array ndims - - - - - - - - - - - - - - - - - - - valint valfloat valchar vallogic x v i n ? ? ?
Programação para executar quádruplas OPWRITE: No switch da função InterpCodIntermed: case OPWRITE: ExecQuadWrite (quad); break; Função ExecQuadWrite (já tem protótipo): voidExecQuadWrite (quadruplaquad) { int i; operando opndaux; pilhaoperandopilhaopndaux; printf ("\n\t\tEscrevendo: \n\n"); InicPilhaOpnd (&pilhaopndaux); for (i = 1; i <= quad->opnd1.atr.valint; i++) { EmpilharOpnd (TopoOpnd (pilhaopnd), &pilhaopndaux); DesempilharOpnd (&pilhaopnd); }
for (i = 1; i <= quad->opnd1.atr.valint; i++) { opndaux = TopoOpnd (pilhaopndaux); DesempilharOpnd (&pilhaopndaux); switch (opndaux.tipo) { case INTOPND: printf ("%d", opndaux.atr.valint); break; case REALOPND: printf ("%g", opndaux.atr.valfloat); break; case CHAROPND: printf ("%c", opndaux.atr.valchar); break; case LOGICOPND: if (opndaux.atr.vallogic == 1) printf ("VERDADE"); else printf ("FALSO"); break; case CADOPND: printf ("%s", opndaux.atr.valcad); break ;
case VAROPND: switch (opndaux.atr.simb->tvar) { case INTEGER: printf ("%d", *(opndaux.atr.simb->valint)); break; case FLOAT: printf ("%g", *(opndaux.atr.simb->valfloat));break; case LOGICAL: if (*(opndaux.atr.simb->vallogic) == 1) printf ("VERDADE"); else printf ("FALSO"); break; case CHAR: printf ("%c", *(opndaux.atr.simb->valchar)); break; } break; } } printf ("\n"); } Executar observando os valores escritos pelas quádruplas OPWRITE
Exercício 5.4: Execução da quádrupla OPMAIS Exemplo: MAIS, (VAR, a), (REAL, 12.5), (VAR, ##1) O problema agora é encontrar o paradeiro dos elementos a serem somados e onde guardar o resultado da soma Tudo depende do tipo do operando e da temporária que vai receber o resultado, que podem ser: Uma variável Uma constante inteira, real, caractere
Programação para executar quádruplas OPMAIS: No switch da função InterpCodIntermed: case OPMAIS: ExecQuadMais (quad); break; Função ExecQuadMais (já tem protótipo): voidExecQuadMais (quadruplaquad) { int tipo1, tipo2, valint1, valint2; float valfloat1, valfloat2; Guardam os valores a serem somados, conforme os tipos Guardam os tipos dos valores a serem somados
switch (quad->opnd1.tipo) { case INTOPND: tipo1 = INTOPND; valint1 = quad->opnd1.atr.valint; break; case REALOPND: tipo1 = REALOPND; valfloat1 = quad->opnd1.atr.valfloat; break; case CHAROPND: tipo1 = INTOPND; valint1 = quad->opnd1.atr.valchar; break; case VAROPND: switch (quad->opnd1.atr.simb->tvar) { case INTEGER: tipo1 = INTOPND; valint1 = *(quad->opnd1.atr.simb->valint); break; case FLOAT: tipo1 = REALOPND; valfloat1=*(quad->opnd1.atr.simb->valfloat);break; case CHAR: tipo1 = INTOPND; valint1 = *(quad->opnd1.atr.simb->valchar); break; } break; }
switch (quad->opnd2.tipo) { case INTOPND: tipo2 = INTOPND; valint2 = quad->opnd2.atr.valint; break; case REALOPND: tipo2 = REALOPND; valfloat2 = quad->opnd2.atr.valfloat; break; case CHAROPND: tipo2 = INTOPND; valint2 = quad->opnd2.atr.valchar; break; case VAROPND: switch (quad->opnd2.atr.simb->tvar) { case INTEGER: tipo2 = INTOPND; valint2 = *(quad->opnd2.atr.simb->valint); break; case FLOAT: tipo2 = REALOPND; valfloat2=*(quad->opnd2.atr.simb->valfloat);break; case CHAR: tipo2 = INTOPND; valint2=*(quad->opnd2.atr.simb->valchar);break; } break; }
switch (quad->result.atr.simb->tvar) { case INTEGER: *(quad->result.atr.simb->valint) = valint1 + valint2; break; case FLOAT: if (tipo1 == INTOPND && tipo2 == INTOPND) *(quad->result.atr.simb->valfloat) = valint1 + valint2; if (tipo1 == INTOPND && tipo2 == REALOPND) *(quad->result.atr.simb->valfloat) = valint1 + valfloat2; if (tipo1 == REALOPND && tipo2 == INTOPND) *(quad->result.atr.simb->valfloat) = valfloat1 + valint2; if (tipo1 == REALOPND && tipo2 == REALOPND) *(quad->result.atr.simb->valfloat) = valfloat1 + valfloat2; break; } } Executar observando os valores escritos de resultados de somas
Exercício 5.5: Execução da quádrupla OPATRIB Exemplo: seja a atribuição: b1 := (i+3 >= j-2) && b2 Suasquádruplaspodem ser: MAIS, (VAR, i), (INT, 3), (VAR, ##1) MENOS, (VAR, j), (INT, 2), (VAR, ##2) GE, (VAR, ##1), (VAR, ##2), (VAR, ##3) AND, (VAR, ##3), (VAR, b2), (VAR, ##4) ATRIB, (VAR, ##4), (IDLE), (VAR, b1) O problema agora é encontrar o paradeiro do valor a ser atribuído e onde guardá-lo Tudo depende dos tipos dos operandos envolvidos
Programação para executar quádruplas OPATRIB: No switch da função InterpCodIntermed: case OPATRIB: ExecQuadAtrib (quad); break; Função ExecQuadAtrib (já tem protótipo): voidExecQuadAtrib (quadruplaquad) { int tipo1, valint1; float valfloat1; char valchar1, vallogic1; Guardam os valores a serem atribuídos, conforme os tipos Guarda o tipo do valor a ser atribuído
switch (quad->opnd1.tipo) { case INTOPND: tipo1 = INTOPND; valint1 = quad->opnd1.atr.valint; break; case REALOPND: tipo1 = REALOPND; valfloat1 = quad->opnd1.atr.valfloat; break; case CHAROPND: tipo1 = CHAROPND; valchar1 = quad->opnd1.atr.valchar; break; case LOGICOPND: tipo1 = LOGICOPND; vallogic1 = quad->opnd1.atr.vallogic; break;
case VAROPND: switch (quad->opnd1.atr.simb->tvar) { case INTEGER: tipo1 = INTOPND; valint1 = *(quad->opnd1.atr.simb->valint); break; case FLOAT: tipo1 = REALOPND; valfloat1=*(quad->opnd1.atr.simb->valfloat);break; case CHAR: tipo1 = CHAROPND; valchar1=*(quad->opnd1.atr.simb->valchar);break; case LOGICAL: tipo1 = LOGICOPND; vallogic1 = *(quad->opnd1.atr.simb->vallogic); break; } break; }
switch (quad->result.atr.simb->tvar) { case INTEGER: if (tipo1 == INTOPND) *(quad->result.atr.simb->valint) = valint1; if (tipo1 == CHAROPND)*(quad->result.atr.simb->valint)=valchar1; break; case CHAR: if (tipo1 == INTOPND) *(quad->result.atr.simb->valchar) = valint1; if (tipo1==CHAROPND)*(quad->result.atr.simb->valchar)=valchar1; break; case LOGICAL: *(quad->result.atr.simb->vallogic) = vallogic1; break; case FLOAT: if (tipo1 == INTOPND) *(quad->result.atr.simb->valfloat) = valint1; if (tipo1 == REALOPND) *(quad->result.atr.simb->valfloat) = valfloat1; if (tipo1 == CHAROPND) *(quad->result.atr.simb->valfloat) = valchar1; break; } } Executar observando os valores das variáveis que recebem atribuição
Exercício 5.6: Execução das quádruplas OPJUMP e OPJF Exemplo: seja o comando enquanto (i < j) i := j + h; Suas quádruplas: 2) NOP, (IDLE), (IDLE), (IDLE) 3) LT, (VAR, i), (VAR, j), (VAR, ##1) 4) JF, (VAR, ##1), (IDLE), (ROTULO, 8) 5) MAIS, (VAR, j), (VAR, h), (VAR, ##2) 6) ATRIB, (VAR, ##2), (IDLE), (VAR, i) 7) JUMP, (IDLE), (IDLE), (ROTULO, 2) 8) NOP, (IDLE), (IDLE), (IDLE)
Nova declaração na função InterpCodIntermed: charcondicao; No switch da função InterpCodIntermed: case OPJUMP: quadprox = quad->result.atr.rotulo; break; case OPJF: if (quad->opnd1.tipo == LOGICOPND) condicao = quad->opnd1.atr.vallogic; if (quad->opnd1.tipo == VAROPND) condicao = *(quad->opnd1.atr.simb->vallogic); if (! condicao) quadprox = quad->result.atr.rotulo; break; Executar observando os desvios efetuados pelos comandos se-senao e enquanto
Exercício 5.7: Execução da quádrupla OPLT No switch da função InterpCodIntermed: case OPLT: ExecQuadLT (quad); break; Função ExecQuadLT (já tem protótipo): voidExecQuadLT (quadruplaquad) { int tipo1, tipo2, valint1, valint2; float valfloat1, valfloat2;
switch (quad->opnd1.tipo) { case INTOPND: tipo1 = INTOPND; valint1 = quad->opnd1.atr.valint; break; case REALOPND: tipo1 = REALOPND; valfloat1=quad->opnd1.atr.valfloat;break; case CHAROPND: tipo1 = INTOPND; valint1 = quad->opnd1.atr.valchar; break; case VAROPND: switch (quad->opnd1.atr.simb->tvar) { case INTEGER: tipo1 = INTOPND; valint1 = *(quad->opnd1.atr.simb->valint); break; case FLOAT: tipo1 = REALOPND; valfloat1 = *(quad->opnd1.atr.simb->valfloat); break; case CHAR: tipo1 = INTOPND; valint1 = *(quad->opnd1.atr.simb->valchar); break; } break; }
switch (quad->opnd2.tipo) { case INTOPND: tipo2 = INTOPND; valint2 = quad->opnd2.atr.valint; break; case REALOPND: tipo2=REALOPND;valfloat2 = quad->opnd2.atr.valfloat;break; case CHAROPND: tipo2 = INTOPND;valint2 = quad->opnd2.atr.valchar; break; case VAROPND: switch (quad->opnd2.atr.simb->tvar) { case INTEGER: tipo2 = INTOPND; valint2 = *(quad->opnd2.atr.simb->valint); break; case FLOAT: tipo2 = REALOPND; valfloat2 = *(quad->opnd2.atr.simb->valfloat); break; case CHAR: tipo2 = INTOPND; valint2 = *(quad->opnd2.atr.simb->valchar); break; } break; }
if (tipo1 == INTOPND && tipo2 == INTOPND) *(quad->result.atr.simb->vallogic) = valint1 < valint2; if (tipo1 == INTOPND && tipo2 == REALOPND) *(quad->result.atr.simb->vallogic) = valint1 < valfloat2; if (tipo1 == REALOPND && tipo2 == INTOPND) *(quad->result.atr.simb->vallogic) = valfloat1 < valint2; if (tipo1 == REALOPND && tipo2 == REALOPND) *(quad->result.atr.simb->vallogic) = valfloat1 < valfloat2; } Eliminar os comentários de pret012013.dat e rodar
Exercício 5.8: Execução da quádrupla READ Exemplo: seja o comando ler (a, b, c); Suas quádruplas: 1) PARAM, (VAR, a), (IDLE), (IDLE 2) PARAM, (VAR, b), (IDLE), (IDLE) 3) PARAM, (VAR, c), (IDLE), (IDLE) 4) READ, (INT, 3), (IDLE), (IDLE)
1) PARAM, (VAR, a), (IDLE), (IDLE 2) PARAM, (VAR, b), (IDLE), (IDLE) 3) PARAM, (VAR, c), (IDLE), (IDLE) 4) READ, (INT, 3), (IDLE), (IDLE)) VAR, c VAR, b VAR, a pilhaoperando (global) Preenchida pelas 3 quádruplas PARAM pilhaopndaux A ser preenchida pela quádrupla READ
Execução da quádrupla: OPREAD, (INT, 3), (IDLE), (IDLE) 1) Desempilhar 3 operandos de pilhaoperando, empilhando-os em pilhaopndaux VAR, c VAR, b VAR, a pilhaoperando (global) Preenchida pelas 3 quádruplas PARAM pilhaopndaux A ser preenchida pela quádrupla READ
Execução da quádrupla: OPREAD, (INT, 3), (IDLE), (IDLE) 2) Desempilhar 3 operandos de pilhaopndaux, lendo valores para seus atributos VAR, a VAR, b VAR, c pilhaoperando (global) Esvaziada pela quádrupla READ pilhaopndaux Preenchida pela quádrupla READ
Leitura dos valores dos operandos: O problema é encontrar o local para guardar o valor lido É uma variável e depende do tipo, que pode ser: Inteiro, real, caractere ou lógico
O valor lido deve ser guardado na TabSimb dims cadeia tid tvar inic ref array ndims - - - - - - - - - - - - - - - - - - - - valint valfloat valchar vallogic A v r s 3 # 5 4 ? ? ? dims cadeia tid tvar inic ref array ndims - - - - - - - - - - - - - - - - - - - valint valfloat valchar vallogic x v i n ? ? ?