810 likes | 1.06k Views
CES-41 COMPILADORES Aulas Práticas - 2014. Capítulo IV Código Intermediário no Yacc. O sub-set da Linguagem COMP-ITA-2014 , usado no final do capítulo anterior, contém: Comandos de atribuição, compostos, entrada e saída, condicionais, while, variáveis escalares e indexadas
E N D
CES-41 COMPILADORESAulas Práticas - 2014 Capítulo IV Código Intermediário no Yacc
O sub-set da Linguagem COMP-ITA-2014, usado no final do capítulo anterior, contém: • Comandos de atribuição, compostos, entrada e saída, condicionais, while, variáveis escalares e indexadas • Seja essa linguagem chamada de Sub-Set2 2014 • Seja o programa a seguir, escrito em Sub-Set2 2014, para encontrar números primos
program primos local {logic achou; int n, div, resto, cont, num;} statements { read (n); write (n, " primeiros numeros naturais primos:\n\n"); if n > 0 then { num = 1; cont = 0; while cont < n do { num = num + 1; div = 2; achou = false; while achou == false && div * div <= num do { resto = num % div; if resto == 0 then achou = true; else div = div + 1; } if achou == false then { write (num, "\n"); cont = cont + 1; } } } }
Um possível código intermediário não otimizado: program primos local {logic achou; int n, div, resto, cont, num;} 1) OPENMOD, (MODULO, primos), (IDLE), (IDLE) read (n); 2) PARAM, (VAR, n), (IDLE), (IDLE) 3) READ, (INT, 1), (IDLE), (IDLE)
NOP: no operation Todos os desvios serão feitos para quádruplas com NOP’s Facilitam a programação Devem ser eliminadas na fase de otimização if n > 0 then { - - - - - } 4) GT, (VAR, n), (INT, 0), (VAR, ##1) 5) JF, (VAR, ##1), (IDLE), (ROTULO, 42) - - - - - 42) NOP, (IDLE), (IDLE), (IDLE) num = 0; cont = 0; 6) ATRIB, (INT, 0), (IDLE), (VAR, num) 7) ATRIB, (INT, 0), (IDLE), (VAR, cont)
NOP: no operation Todos os desvios serão feitos para quádruplas com NOP’s Facilitam a programação Devem ser eliminadas na fase de otimização while cont < n do { - - - - - } 8) NOP, (IDLE), (IDLE), (IDLE) 9) LT, (VAR, cont), (VAR, n), (VAR, ##2) 10) JF, (VAR, ##2), (IDLE), (ROTULO, 41) - - - - - 40) JUMP, (IDLE), (IDLE), (ROTULO, 8) 41) NOP, (IDLE), (IDLE), (IDLE) num = num + 1; div = 2; achou = false; 11) MAIS, (VAR, num), (INT, 1), (VAR, ##3) 12) ATRIB, (VAR, ##3), (IDLE), (VAR, num) 13) ATRIB, (INT, 2), (IDLE), (VAR, div) 14) ATRIB, (LOGIC, 0), (IDLE), (VAR, achou)
while achou == false && div * div <= num do { - - - - - } 15) NOP, (IDLE), (IDLE), (IDLE) 16) EQ, (VAR, achou), (LOGIC, 0), (VAR, ##4) 17) MULT, (VAR, div), (VAR, div), (VAR, ##5) 18) LE, (VAR, ##5), (VAR, num), (VAR, ##6) 19) AND, (VAR, ##4), (VAR, ##6), (VAR, ##7) 20) JF, (VAR, ##7), (IDLE), (ROTULO, 32) - - - - - 31) JUMP, (IDLE), (IDLE), (ROTULO, 15) 32) NOP, (IDLE), (IDLE), (IDLE)
resto = num % div; if resto == 0 then achou = true; else div = div + 1; 21) RESTO, (VAR, num), (VAR, div), (VAR, ##8) 22) ATRIB, (VAR, ##8), (IDLE), (VAR, resto) 23) EQ, (VAR, resto), (INT, 0), (VAR, ##9) 24) JF, (VAR, ##9), (IDLE), (ROTULO, 27) 25) ATRIB, (LOGIC, 1), (IDLE), (VAR, achou) 26) JUMP, (IDLE), (IDLE), (ROTULO, 30) 27) NOP, (IDLE), (IDLE), (IDLE) 28) MAIS, (VAR, div), (INT, 1), (VAR, ##10) 29) ATRIB, (VAR, ##10), (IDLE), (VAR, div) 30) NOP, (IDLE), (IDLE), (IDLE)
if achou == false then {write (num); cont = cont + 1;} 33) EQ, (VAR, achou), (LOGIC, 0), (VAR, ##11) 34) JF, (VAR, ##11), (IDLE), (ROTULO, 39) 35) PARAM, (VAR, num), (IDLE), (IDLE) 36) WRITE, 1, (IDLE), (IDLE) 37) MAIS, (VAR, cont), (INT, 1), (VAR, ##12) 38) ATRIB, (VAR, ##12), (IDLE), (VAR, cont) 39) NOP, (IDLE), (IDLE), (IDLE) Cada linha do código intermediário chama-se quádrupla
Estrutura de dados para o código intermediário de linguagens contendo subprogramas:
Cada lista de quádruplas fica acoplada a um mod-head As quádruplas do módulo de tipo glob destinam-se a: Alocar as variáveis globais Acionar a execução do módulo principal (main) E encerrar tudo no final Cada lista de quádruplas tem um nó-lider
Estrutura de dados para código intermediário de linguagens sem subprogramas: O primeiro mod-head seria dispensável Ele é usado prevendo a implementação do módulo global
A primeira quádrupla é uma quádrupla-líder, sem conteúdo Esta estrutura será usada nas próximas aulas No projeto será usada a do slide anterior
Programa 4.1: • O arquivo inter012014.y tem um analisador semântico quase acabado para a linguagem Sub-Set2 2014 • O arquivo inter012014.l é seu analisador léxico • O arquivo inter012014.y contém ainda declarações e funções para auxiliar a construção do código intermediário • Rodar esses arquivos com inter012014.dat • A seguir, uma descrição dessas declarações e funções
celquad • Estrutura de uma quádrupla: typedefstructcelquadcelquad; typedefcelquad *quadrupla; structcelquad { int num, oper; operando opnd1, opnd2, result; quadruplaprox; }; • O campo num numera as quádruplas • Não é necessário na compilação • Foi colocado por razões didáticas • Um operando tem dois campos: o tipo e o atributo oper opnd1 num opnd2 result prox
operando tipo atr (simb, valint, valfloat, valchar, vallogic, valcad, rotulo, modulo) • Estrutura de um operando: typedefstructoperandooperando; structoperando { inttipo; atribopndatr; }; • Os tipos dos operandos são: • Operando vazio (idle) • Nome de variável • Constantes inteira, real, lógica e caractere • Cadeia de caracteres • Rótulo de quádruplas (ponteiro para quádruplas) • Nome de módulo
operando tipo atr (simb, valint, valfloat, valchar, vallogic, valcad, rotulo, modulo) • Estrutura do atributo de um operando: typedef union atribopndatribopnd; union atribopnd { simbolosimb; intvalint; float valfloat; char valchar; char vallogic; char *valcad; quadruplarotulo; modhead modulo; }; • Tabela dos atributos conforme o tipo de operando:
celmodhead modname • Estrutura de um cabeçalho de módulo: typedef struct celmodheadcelmodhead; typedefcelmodhead *modhead; struct celmodhead { simbolo modname; modhead prox; int modtip; quadruplalistquad; }; modtip prox listquad
Variáveis globais: quadruplaquadcorrente, quadaux; modhead codintermed, modcorrente; intoper, numquadcorrente; operando opnd1, opnd2, result, opndaux; intnumtemp; const operandoopndidle = {IDLEOPND, 0}; • quadcorrente: ponteiro para a celquad recém-alocada • codintermed: ponteiro que dá acesso a todo o código intermediário • modcorrente: ponteiro para a última celmodhead alocada • numtemp: usada para controlar a concessão de nomes a variáveis temporárias • opndidle: constante do tipo operando para preencher operandos vazios nas quádruplas
#define IDPROG 1 #define IDVAR 2 #define IDFUNC 3 #define IDPROC 4 #define IDGLOB 5 • Novas definições para os tipos dos identificadores: • Relembrando as definições para os tipos de variáveis: #define NAOVAR 0 #define INTEIRO 1 #define LOGICO 2 #define REAL 3 #define CARACTERE 4
Definições de constantes para operadores de quádruplas: #define OPOR 1 #define OPAND 2 #define OPLT 3 #define OPLE 4 #define OPGT 5 #define OPGE 6 #define OPEQ 7 #define OPNE 8 #define OPMAIS 9 #define OPMENOS 10 #define OPMULTIP 11 #define OPDIV 12 #define OPRESTO 13 #define OPMENUN 14 #define OPNOT 15 #define OPATRIB 16 #define OPENMOD 17 #define NOP 18 #define OPJUMP 19 #define OPJF 20
Definições de constantes para os tipos dos operandos: #define IDLEOPND 0 #define VAROPND 1 #define INTOPND 2 #define REALOPND 3 #define CHAROPND 4 #define LOGICOPND 5 #define CADOPND 6 #define ROTOPND 7 #define MODOPND 8
Protótipos das funções para o código intermediário: void InicCodIntermed (void); void InicCodIntermMod (simbolo); quadruplaGeraQuadrupla (int, operando, operando, operando); simboloNovaTemp (int); voidImprimeQuadruplas (void); voidRenumQuadruplas (quadrupla, quadrupla);
codintermed modname modtip ● • Função InicCodIntermed: void InicCodIntermed () { codintermed = malloc (sizeof (celmodhead)); modcorrente = codintermed; modcorrente->listquad = NULL; modcorrente->prox = NULL; } prox ● listquad modcorrente
codintermed simb tid cadeia modname modname modtip modtip ● ● • Função InicCodIntermMod (simbolosimb): void InicCodIntermMod (simbolosimb) { modcorrente->prox = malloc (sizeof (celmodhead)); modcorrente = modcorrente->prox; modcorrente->prox = NULL; modcorrente->modname = simb; modcorrente->modtip = simb->tid; modcorrente->listquad = malloc (sizeof (celquad)); quadcorrente = modcorrente->listquad; quadcorrente->prox = NULL; numquadcorrente = 0; quadcorrente->num = numquadcorrente; } prox prox ● listquad listquad modcorrente num oper #### opnd1 #### opnd2#### result #### prox 0 ● quadcorrente
Função GeraQuadrupla (oper, opnd1, opnd2, result): quadruplaGeraQuadrupla (intoper, operando opnd1, operando opnd2, operando result) { quadcorrente->prox = malloc (sizeof (celquad)); quadcorrente = quadcorrente->prox; quadcorrente->oper = oper; quadcorrente->opnd1 = opnd1; quadcorrente->opnd2 = opnd2; quadcorrente->result = result; quadcorrente->prox = NULL; numquadcorrente ++; quadcorrente->num = numquadcorrente; return quadcorrente; } num 452 oper X opnd1 YYYY opnd2 ZZZZ result WWWW prox num oper opnd1 opnd2 result prox ● quadcorrente 453 R SSSS TTTT valor retornado VVVV opnd2 result opnd1 oper R VVVV SSSS TTTT ●
Gera um nome para uma nova variável temporária O nome começa com ## e termina com um número O número é a soma do último utilizado +1 O nome é inserido na TabSimb e marcado como inicializado e referenciado Retorna um ponteiro para a célula inserida na TabSimb • Função Novatemp (tip): simboloNovaTemp (inttip) { simbolosimb; inttemp, i, j; charnometemp[10] = "##", s[10] = {0}; numtemp ++; temp = numtemp; for (i = 0; temp > 0; temp /= 10, i++) s[i] = temp % 10 + '0'; i --; for (j = 0; j <= i; j++) nometemp[2+i-j] = s[j]; simb = InsereSimb (nometemp, IDVAR, tip); simb->inic = simb->ref = VERDADE; simb->array = FALSO;return simb; } O tipo da nova variável vem como argumento
Os operandos para as quádruplas que calculam o valor de expressões devem estar nos atributos dos não-terminais para a montagem de expressões • Exemplo: seja a produção Term : Term MULTOP Factor Deve ser gerada a quádrupla com: operador correspondente ao atributo de MULTOP primeiro operando: no atributo de Termdo lado direito segundo operando: no atributo de Factor operando resultado: no atributo de Termdo lado esquerdo
Não-terminais para a montagem de expressões: Expression, AuxExpr1, AuxExpx2, AuxExpr3, AuxExpr4, Term, Factor • Esses não-terminais já guardam seus tipos em seus atributos, para os testes de compatibilidade entre operadores e operandos: %type <tipoexpr> Expression AuxExpr1 AuxExpx2 AuxExpr3 AuxExpr4 TermFactor • Agora, o atributo desses não-terminais deverá ter dois campos: o tipo e o operando
Seja também a produção: Factor : Variable • É conveniente também que o não-terminalVariablecarregue um operando em seu atributo, para que o operando de Factor receba esse operando • Esse não-terminal já guarda em seu atributo um ponteiro para a TabSimb, para os testes de compatibilidade entre operadores e operandos: %type <simb> Variable • Agora, o atributo desse não-terminal deverá ter dois campos: o referido ponteiro e o operando
Novas declarações para atributos de expressões e variáveis: typedefstructinfoexpressaoinfoexpressao; structinfoexpressao { int tipo; operando opnd; }; typedefstructinfovariavelinfovariavel; structinfovariavel { simbolosimb; operando opnd; }; • Novos campos para a declaração %union: %union { char cadeia[50]; int atr, valint; float valreal; charcarac; infoexpressaoinfoexpr; infovariavelinfovar; simbolosimb; int dim; int nsubscr; }
Novos campos para a declaração %union: %union { char cadeia[50]; int atr, valint; float valreal; charcarac; infoexpressaoinfoexpr; infovariavelinfovar; simbolosimb; int dim; int nsubscr; } • Novas especificações para os atributos dos não-terminais envolvidos: %type <infovar> Variable %type <infoexpr> Expression AuxExpr1 AuxExpr2 AuxExpr3 AuxExpr4 TermFactor Antes era <simb> Antes era <tipoexpr>
Nas produções onde aparecem não-terminais envolvidos no cálculo de expressões foi trocado $$ por $$.tipo e $1, $2, etc. por $1.tipo, $2.tipo, etc. • Nas ocorrências de atributos do não terminal Variable, foi trocado $$ por $$.simb e $1, $2, etc. por $1.simb, $2.simb, etc.
Exercício 4.1: Inicializar o código intermediário e a lista de quádruplas do único módulo,imprimindo-as (a lista é vazia) • Na produção do não-terminalProg: Prog : { InicTabSimb (); InicCodIntermed (); numtemp = 0; } PROGRAM ID {printf ("program %s\n", $3); simb = InsereSimb ($3, IDPROG, NAOVAR); InicCodIntermMod (simb); } LocDeclsStats { VerificaInicRef (); ImprimeTabSimb (); ImprimeQuadruplas (); } ; Acrescentar ao arquivo inter012014.y o código em destaque Rodar com o arquivo inter012014.dat
Exercício 4.2: Inserir a quádrupla OPENMOD • Ainda na produção do não-terminalProg: Prog: {- - - - -} PROGRAM ID { - - - - - InicCodIntermMod (simb); opnd1.tipo = MODOPND; opnd1.atr.modulo = modcorrente; GeraQuadrupla (OPENMOD, opnd1, opndidle, opndidle); } LocDeclsStats { - - - - - } ; Acrescentar ao arquivo inter012014. y o código em destaque Rodar com o arquivo inter012014. dat
Exercício 4.3: Inserir quádruplas para expressões sem variáveis indexadas • Exemplo: as quádruplas para a expressão: (i+3 >= j-2) && b2 sendo: int i, j; logic b2; podem 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)
É necessária uma programação em todos os não-terminais envolvendo expressões • Em toda produção que contribui para a formação de expressões, deve-se calcular o atributo operando do não-terminal do lado esquerdo • Numa produção onde aparece operador de expressões, além desse cálculo, deve-se gerar quádrupla com a operação correspondente e com os atributos operandos dos não-terminais de expressões
Na 1ª produção do não-terminalVariable(sem subscritos), calcula-se seu operando: Variable: ID { - - - - - $$.simb = simb; if ($$.simb != NULL) { if ($$.simb->array == VERDADE) if (usastr == FALSO) Esperado ("Subscrito\(s)"); else if ($$.simb->ndims != 1) Esperado ("Subscrito\(s)"); $$.opnd.tipo = VAROPND; $$.opnd.atr.simb = $$.simb; } }
Nas produções sem operadores do não-terminalFactor, calcula-se seu operando: Factor : Variable { if ($1.simb != NULL) { - - - - - $$.tipo = $1.simb->tvar; $$.opnd = $1.opnd; } } | INTCT { - - - - - $$.tipo = INTEIRO; $$.opnd.tipo = INTOPND; $$.opnd.atr.valint = $1; } | FLOATCT { - - - - - $$.tipo = REAL; $$.opnd.tipo = REALOPND; $$.opnd.atr.valfloat = $1; }
Factor: CHARCT { - - - - - $$.tipo = CARACTERE; $$.opnd.tipo = CHAROPND; $$.opnd.atr.valchar = $1; } | TRUE { - - - - - $$.tipo = LOGICO; $$.opnd.tipo = LOGICOPND; $$.opnd.atr.vallogic = 1; } | FALSE { - - - - - $$.tipo = LOGICO; $$.opnd.tipo = LOGICOPND; $$.opnd.atr.vallogic = 0; } | OPPAR {- - - - -} Expression CLPAR { - - - - - $$.tipo = $3.tipo; $$.opnd = $3.opnd; }
Na produção do não-terminalFactor, com o operador NEG gera-se uma quádrupla, além de calcular o operando do lado esquerdo: Factor : NEG {- - - - -} Factor { if ( - - - - - ) Incompatibilidade ( - - - - - ); if ($3.tipo == REAL) $$.tipo = REAL; else $$.tipo = INTEIRO; $$.opnd.tipo = VAROPND; $$.opnd.atr.simb = NovaTemp ($$.tipo); GeraQuadrupla (OPMENUN, $3.opnd, opndidle, $$.opnd); } ; Rodar com o arquivo inter012014. dat
Rodar com o arquivo inter012014. dat • Produções do não-terminalTerm: Term : Factor/* default: $$ = $1 */ | Term MULTOP {- - - - -} Factor { switch ($2) { case MULT: case DIV: - - - - - $$.opnd.tipo = VAROPND; $$.opnd.atr.simb = NovaTemp ($$.tipo); if ($2 == MULT) GeraQuadrupla (OPMULTIP, $1.opnd, $4.opnd, $$.opnd); elseGeraQuadrupla (OPDIV, $1.opnd, $4.opnd, $$.opnd); break; case RESTO: - - - - - $$.opnd.tipo = VAROPND; $$.opnd.atr.simb = NovaTemp ($$.tipo); GeraQuadrupla (OPRESTO, $1.opnd, $4.opnd, $$.opnd); break; } } ; Fazer programação análoga para as produções dos não terminais ExprAux4, ExprAux3, ExprAux2, ExprAux1 e Expressao
Exercício 4.4: Inserir quádruplas para comandos de atribuição • Exemplo: as quádruplas para a atribuição: b1 = (i+3 >= j-2) && b2 sendo: int i, j; logic b1, b2; podem 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)
Exercício 4.5: Inserir quádruplas para comandos condicionais • Primeiramente, sejam os comandos if sem else • Exemplo: as quádruplas para o comando if i < j then { i = i+3; n = v/h; } podem ser: 2) LT, (VAR, i), (VAR, j), (VAR, ##1) 3) JF, (VAR, ##1), (IDLE), (ROTULO, 8) 4) MAIS, (VAR, i), (INT, 3), (VAR, ##2) 5) ATRIB, (VAR, ##2), (IDLE), (VAR, i) 6) DIV, (VAR, v), (VAR, h), (VAR, ##3) 7) ATRIB, (VAR, ##3), (IDLE), (VAR, n) 8) NOP, (IDLE), (IDLE), (IDLE) Usar os arquivos inter022014.y, inter022014.l e inter022014.dat
Arquivo inter022014.y: • Gera quádruplas para abrir módulo, operadores de expressões e comandos de atribuição • FunçãoRenumQuadruplas (q1, q2): • Algumas quádruplas serão geradas fora de ordem • Sua ordem deve ser corrigida • Para fins didáticos, as quádruplas serão renumeradas • Abrir o arquivo inter022014.dat • Rodar flex, yacc, gcc e executável gerado • A cada implementação, alterar os comentários em inter022014.dat
A programação para if sem else está na produção do não-terminalIfStat: IfStat : IF {- - - - -} Expression { - - - - - opndaux.tipo = ROTOPND; $<quad>$ = GeraQuadrupla (OPJF, $3.opnd, opndidle, opndaux); } THEN {printf ("then\n");} Statement{ $<quad>4->result.atr.rotulo = GeraQuadrupla (NOP, opndidle, opndidle, opndidle); }ElseStat ; É necessário acrescentar o campo quadna declaração %union:quadruplaquad;
A programação para if sem else está na produção do não-terminalIfStat: IfStat : IF {- - - - -} Expression { - - - - - opndaux.tipo = ROTOPND; $<quad>$ = GeraQuadrupla (OPJF, $3.opnd, opndidle, opndaux); } THEN {printf ("then\n");} Statement{ $<quad>4->result.atr.rotulo = GeraQuadrupla (NOP, opndidle, opndidle, opndidle); }ElseStat ; No arquivo inter022014.dat, tirar o comentário do if sem else e rodar
IfStat : IF {- - - - -} Expression { - - - - - opndaux.tipo = ROTOPND; $<quad>$ = GeraQuadrupla (OPJF, $3.opnd, opndidle, opndaux); } THEN {printf ("then\n");} Statement{ $<quad>4->result.atr.rotulo = GeraQuadrupla (NOP, opndidle, opndidle, opndidle); }ElseStat ; $4 Expression ----- JF NOP ----- ##n ----- ----- ----- ----- ##n ----- ROT A quádrupla destino do desvio é gerada na produção de IfStat NOP é eliminada na otimização do código intermediário ? Statement
Sejam agora os comandos if com else • Exemplo: as quádruplas para os comandos if i < j then { i = i+3; n = v/h; } else { v = h; i = j; } n = v; podem ser: 2) LT, (VAR, i), (VAR, j), (VAR, ##1) 3) JF, (VAR, ##1), (IDLE), (ROTULO, 9) 4) MAIS, (VAR, i), (INT, 3), (VAR, ##2) 5) ATRIB, (VAR, ##2), (IDLE), (VAR, i) 6) DIV, (VAR, v), (VAR, h), (VAR, ##3) 7) ATRIB, (VAR, ##3), (IDLE), (VAR, n) 8) JUMP, (IDLE), (IDLE), (ROTULO, 12) 9) NOP, (IDLE), (IDLE), (IDLE) 10) ATRIB, (VAR, h), (IDLE), (VAR, v) 11) ATRIB, (VAR, j), (IDLE), (VAR, i) 12) NOP, (IDLE), (IDLE), (IDLE) 13) ATRIB, (VAR, v), (IDLE), (VAR, n)
A programação para if com else está nas produções dos não-terminais IfStat e ElseStat: IfStat : IF { - - - - - } Expression { - - - - - opndaux.tipo = ROTOPND; $<quad>$ = GeraQuadrupla (OPJF, $3.opnd, opndidle, opndaux); } THEN {printf ("then\n");} Statement { $<quad>$ = quadcorrente; $<quad>4->result.atr.rotulo = GeraQuadrupla (NOP, opndidle, opndidle, opndidle); } ElseStat{ if ($<quad>8->prox != quadcorrente) { quadaux = $<quad>8->prox; $<quad>8->prox = quadaux->prox; quadaux->prox = $<quad>8->prox->prox; $<quad>8->prox->prox = quadaux; RenumQuadruplas ($<quad>8, quadcorrente); } } ;