• Nenhum resultado encontrado

4.3 Dolphin Internal Representation

4.3.2 Aplica¸ c˜ ao da DIR

Pretende-se nesta sec¸c˜ao ilustrar como utilizar a DIR na constru¸c˜ao da RIC, esperando assim ajudar a compreender o papel das diversas classes e da pr´opria estrutura deste modelo. O programa de Figura4.7, que est´a implementado em linguagem C, ´e utilizado como exemplo para a constru¸c˜ao da RIC.

A constru¸c˜ao da RIC desenrola-se normalmente em quatro fases, a saber: instancia¸c˜ao dos objectos de Program e de Function, os dois n´ıveis mais altos de abstrac¸c˜ao; instanci- a¸c˜ao e preenchimento das tabelas de identificadores (IdentTable); constru¸c˜ao do GFC, o que passa pela instancia¸c˜ao dos FlowNodes; e por fim, a instancia¸c˜ao dos Expressions, que correspondem ao n´ıvel mais baixo de abstrac¸c˜ao.

Assim, a primeira classe a instanciar ´e Program, pois ser´a esse objecto que vai conter toda a RIC. Posteriormente, e dependendo do tipo de linguagem fonte, ter-se-´a que instanciar um ou mais objectos do tipo Function. Neste caso tratando-se de um programa escrito em linguagem C, considera-se que todas as fun¸c˜oes est˜ao ao mesmo n´ıvel (n˜ao h´a encadeamento de fun¸c˜oes), assim pode-se logo `a partida instanciar dois objectos: um para a fun¸c˜ao swap(...) e outro para a fun¸c˜ao test(...), conforme ilustra a Figura4.8.

(a) Utiliza¸c˜ao de ArgExpr. (b) Utiliza¸c˜ao de LExpr.

Figura 4.6: Utiliza¸c˜ao de operadores com mais do que dois operandos.

(1) void swap(int *a,int *b){...}

(2) int test(int x,int y){

(3) if(x>y){

(4) swap(&x,&y);

(5) return 1;

(6) }else return 0;

(7) }

Figura 4.7: Programa utilizado como exemplo para construir a RIC.

test(...) na tabela de identificadores. A primeira opera¸c˜ao a ser efectuada consiste em definir o tipo da fun¸c˜ao, o que ´e feito atrav´es de CTCProcedure, que requer a defini¸c˜ao do tipo de retorno e dos tipos dos parˆametros. O tipo da fun¸c˜ao, que ´e representado por tf2, ´e depois utilizado para definir o s´ımbolo que vai representar a fun¸c˜ao test(...) (vari´avel sp2 ). O ´ultimo procedimento a executar ´e inserir o s´ımbolo na tabela de identificadores.

Poder´a ainda ser necess´ario preencher as tabelas de identificadores das fun¸c˜oes. Este procedimento encontra-se ilustrado na Figura 4.10 para a fun¸c˜ao test(...), em que se insere na tabela de identificadores informa¸c˜ao sobre os parˆametros.

O passo seguinte consiste em construir o GFC. Para tal utiliza-se o objecto da classe CFG, que como ´e automaticamente criado aquando da instancia¸c˜ao de Function, apenas tem

(1) Program *p=new Program();

(2) Function *f1=new Function(p,"swap",FUNCTION,...);

(3) Function *f2=new Function(p,"test",FUNCTION,...);

4.3. Dolphin Internal Representation 59

(1) ...

(2) IdentTable<CellTable*> *tid=p->getIdentTable(); // Acesso à tab. de

(3) // identificadores global

(4) CTCProcedure *tf2=new CTCProcedure(); // Definição do tipo

(5) // para a função test(...)

(6) tf2->setType(new CTInt()); // Atribuição do tipo

(7) // de retorno de tf2

(8) tf2->add(new CTInt()); // Atribuição do tipo

(9) // do 1o parâmetro

(10) tf2->add(new CTInt()); // Atribuição do tipo

(11) // do 2o parâmetro

(12) CSProcedure *sp2=new CSProcedure(tf2); // Instanciação do símbolo

(13) tid->ins("test",sp2); // Inserção de sp2 na

(14) // tab. de identificadores

Figura 4.9: Preenchimento da tabela de identificadores global.

(1) ...

(2) tid=f2->getIdentTable(); // Acesso à tab. de

(3) // identificadores de f2

(4) CTInt *tpx=new CTInt(); // Definição do tipo

(5) // do 1o parâmetro

(6) CSParameter *spx=new CSParameter(tpx) // Instanciação do símbolo

(7) // para o 1o parâmetro;

(8) tid->ins("x",spx); // Inserção do 1o parâmetro

(9) // na tab. de identificadores

(10) CTInt *tpy=new CTInt(); // Definição do tipo

(11) // do 2o parâmetro

(12) CSParameter *spy=new CSParameter(tpy); // Instanciação do símbolo

(13) // para o 2o parâmetro;

(14) tid->ins("y",spy); // Inserção do 2o parâmetro

(15) // na tab. de identificadores

Figura 4.10: Preenchimento da tabela de identificadores local.

que ser preenchido (com objectos do tipo FlowNode). A Figura 4.11(b)ilustra a constru¸c˜ao parcial do GFC para a fun¸c˜ao test(...), cujo fluxograma se encontra representado na Fi- gura 4.11(a). Cada FlowNode assegura automaticamente a instancia¸c˜ao da respectiva Label e express˜ao de fluxo de controlo.

Para concluir, falta construir as ´arvore de express˜oes, o que passa por instanciar ob- jectos das classes derivadas de Expression. A Figura4.12ilustra esta opera¸c˜ao para a fun¸c˜ao test(...). ´E de real¸car que a instancia¸c˜ao de cada Expression, se faz passando o nodo ao qual a express˜ao pertence. O que serve para registar a express˜ao no LDT associado ao nodo. ´E

(a) Fluxograma da fun¸c˜ao test(...).

(1) ...

(2) CFG *g2=f2->getCFG();

(3) JNode *n1=new JNode(g2);

(4) CJNode *n2=new CJNode(g2);

(5) RNode *n3=new RNode(g2);

(6) RNode *n5=new RNode(g2);

(7) JNode *n4=new JNode(g2);

(8) JNode *n6=new JNode(g2);

(9) RNode *n7=new RNode(g2);

(10) n1->setOut(n2);

(11) n2->setOut(n3);

(12) n2->setCOut(n5);

(13) n4->setOut(n7);

(14) n6->setOut(n7);

(b) Instancia¸c˜ao das classes.

Figura 4.11: Constru¸c˜ao do GFC da fun¸c˜ao test(...).

ainda importante real¸car que os objectos do tipo DT s˜ao implicitamente criados aquando das Expressions, processo que ´e completamente transparente para o utilizador. H´a apenas que ter em aten¸c˜ao que a ordem pela qual se criam as Expressions, vai influenciar a ordem dos DTs em LDT. Por exemplo, n˜ao ´e conveniente substituir as linhas 7 e 10 da Figura4.12, pela seguinte express˜ao: le→ins(0,new Memory(n3,”x”)). Isto porque neste caso, o objecto Memory ´e criado ap´os o objecto LExpr, pelo que na lista de DTs surge primeiro o DT de LExpr e s´o posteriormente ´e que surge o DT de Memory, o que poder´a n˜ao fazer muito sentido.

A partir deste ponto ´e j´a poss´ıvel utilizar a RIC, podendo esta ser acedida no seu todo atrav´es da vari´avel p (apontador para Program), ou parcialmente, a partir de p ou directamente das vari´aveis utilizadas na instancia¸c˜ao dos objectos.

Poder´a ainda ser ´util encapsular Program na classe DIR, o que permite juntar num ´

unico objecto, a RIC e as op¸c˜oes de compila¸c˜ao. Para tal basta efectuar as opera¸c˜oes da Figura 4.13. As op¸c˜oes de compila¸c˜ao s˜ao armazenadas num dicion´ario cuja chave e o valor associado `a chave, s˜ao ambos do tipo string.

´

E tamb´em poss´ıvel preencher automaticamente o dicion´ario com os parˆametros de compila¸c˜ao, bastando registar no objecto DIR os argumentos utilizados na linha de comando da invoca¸c˜ao do compilador. ´E tamb´em poss´ıvel passar este objecto, directamente ao front- end, que ap´os construir a RIC, tratar´a de efectuar automaticamente a anexa¸c˜ao de Program. Ambas as opera¸c˜oes encontram-se representadas na Figura4.14.