Ir para o conteúdo
  • Revista PROGRAMAR: Já está disponível a edição #60 da revista programar. Faz já o download aqui!

jett

Gerador de Código Intermediário

Mensagens Recomendadas

jett

Olá, estou tentando construir um compilador, já fiz o analisador léxico com o flex e o analisador sintático com o bison, agora preciso fazer um gerador de código intermediário, mas não tenho ideia de como se faz, estava vendo seguinte código, mas ele gera código assembly, mas eu preciso de código intermediário. Alguma ideia?

Exemplo de uma especificação YACC
(calculadora de mesa simples)
G:  E   E  +  T   |   T
  T   T  *  F   |    F
  F    (  E  )    |   digito
% { #include <ctype.H> } %
%  token digito
%%
line : E ‘\n’ {printf ($1)}
E    : E  ‘+’  T	  { $$ = $1 + $3;}
   |  T				 { $$ = $1 }
   ;
T    : T  ‘*’  F	  { $$ = $1 * $3;}
   |  F				 { $$ = $1;}
   ;
F    : ‘(‘  E  ‘)’	 { $$ = $2;}
   |  digito		 { $$ = $1;}
   ;
%%
YYLEX ( )  (* analisador léxico *)
{  int c;
   c = getchar(c);
   if (isdigit(c))
    { YYVAL = c – ‘0’;
	   return digito }
   return c; }

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
KTachyon

O teu problema é maior do que aquilo que eu acho que pensas. Em primeiro lugar, só para ter a certeza que sabes o que é que significa "código intermédio":

Quando o teu "compilador" vai executar, vai separar as instruções em partes tratáveis. Essas partes tratáveis são depois colocadas em estruturas (listas ligadas) que são aquilo a que se dá o nome de código intermédio.

Em vez de fazeres:

line : E ‘\n’        {printf ($1)}
E    : E  ‘+’  T     { $$ = $1 + $3;}
    |  T            { $$ = $1 }
    ;

Utilizas funções para inserir numa estrutura:

%{
#include <ctype.H>
#include "structures.h"
#include "functions.h"

e_statement *myprogram;
%}
%  token digito
%%
line : E ‘\n’        { myprogram = $1 }
E    : E  ‘+’  T     { $$ = insert_e($2, $1); }
    |  T            { $$ = insert_e($1); }
    ;
// etc... 

Aquilo que vai estar a ser apontado pelo ponteiro myprogram é o teu código intermédio.

Agora só tens que criar estruturas para reter aquilo que vires que é necessário e implementares as funções para inserir os nós às listas.


“There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.”

-- Tony Hoare

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
jett

Bom, o que eu deveria fazer após o analisador léxico e sintático, seria um código intermediário para toda a linguagem C. A minha principal dúvida é de como fazer um código intermediário para as funções e todas as expressões desta linguagem de programação. Segue abaixo o meu analisador léxico e o meu analisador sintático.

analisador léxico:

D   [0-9]
L   [a-zA-Z_]
%{
  #include <stdio.h>
  #include <stdlib.h>
  #include "c.tab.h"
  void count();
%}
%%
"int"  { count(); return(INT); }
"float"  { count(); return(FLOAT); }
"char"  { count(); return(CHAR); }
"double" { count(); return(DOUBLE); }
"if"  { count(); return(IF); }
"else"  { count(); return(ELSE); }
"while"  { count(); return(WHILE); }
"for"  { count(); return(FOR); }
"do"  { count(); return(DO); }
"void"  { count(); return(VOID);}
"main"  { count(); return(MAIN);}

"||"  { count(); return(OP_OR); }
"&&"  { count(); return(OP_AND); }
"=="  { count(); return(OP_EQ); }
"!="  { count(); return(OP_NQ); }
"<="  { count(); return(OP_LE); }
">="  { count(); return(OP_GE); }
{L}({L}|{D})* { count(); return(VAR); }
{D}+  { count(); return(CONST); }
{D}+"."{D}+ { count(); return(CONST); }
"{"  { count(); return('{'); }
"}"  { count(); return('}'); }
";"  { count(); return(';'); }
","  { count(); return(','); }
"="  { count(); return('='); }
"<"  { count(); return('<'); }
">"  { count(); return('>'); }
"-"  { count(); return('-'); }
"+"  { count(); return('+'); }
"*"  { count(); return('*'); }
"/"  { count(); return('/'); }
"("  { count(); return('('); }
")"  { count(); return(')'); }
"!"  { count(); return('!'); }
[ \t\v\n\f] { count(); }
.  {  }
%%

int yywrap( ){
return(1);
}
int column = 0;
void count()
{
int i;
for (i = 0; yytext[i] != '\0'; i++)
 if (yytext[i] == '\n')
  column = 0;
 else if (yytext[i] == '\t')
  column += 8 - (column % 8);
 else
  column++;
ECHO;
}

analisador sintático:

%{
  #include <stdio.h>
  #include <string.h>
  #include "y.tab.h"
  void yyerror(char *);
  char* arg;
%}
%token INT FLOAT CHAR DOUBLE
%token VOID MAIN
%token IF WHILE FOR DO
%nonassoc ELSE
%token VAR CONST
%left OP_OR OP_AND
%left OP_GE OP_LE OP_EQ OP_NQ '>' '<'
%left '+' '-'
%left '*' '/'
%nonassoc EXPR_UN
%start prog
%%
prog: lista_cmd
  ;
lista_cmd: cmd
  |  lista_cmd cmd
  ;
cmd: decl_comp
  | cmd_expr
  | cmd_comp
  | sel
  | loop
  | func
  ;
cmd_comp: '{' lista_cmd '}'
  ;
sel: IF '(' expr ')' lista_cmd else_op
  ;
else_op: ELSE lista_cmd
  |
  ;
func:  tipo VAR'(' param ')'
  | VAR'(' param ')' ';'
  ;
loop: WHILE '(' expr ')' lista_cmd
  | FOR '('cmd_expr cmd_expr expr ')' lista_cmd
  | DO lista_cmd WHILE '(' expr ')' ';'
  ;
decl_comp: decl
  |  decl_comp decl
  ;
param: decl_comp
  |   lista_var
  |
  ;
decl: tipo lista_var pnt
   ;
pnt: ';' {FILE *arquivo;
	  arquivo = fopen(arg,"a");
  fputs("AAA",arquivo);}
  | ','
  | 
  ;
tipo: INT
  | FLOAT
  | CHAR
  | DOUBLE
  | VOID
  | MAIN
  ;
lista_var: var_inic
  |  lista_var ',' var_inic
  ;
var_inic: VAR
  |  VAR '=' expr
  ;
cmd_expr: ';'
  |  expr ';'
  ;
expr: VAR '=' expr
  | expr OP_OR expr
  |  expr OP_AND expr
  |  expr OP_GE expr
  |  expr OP_LE expr
  |  expr OP_NQ expr
  |  expr OP_EQ expr
  |  expr '<' expr
  |  expr '>' expr
  |  expr '+' expr
  |  expr '-' expr
  |  expr '*' expr
  |  expr '/' expr
  |  un_op expr %prec EXPR_UN
  | CONST
  |  VAR
  |  '(' expr ')'
  ;
un_op: '-'
  | '!'
  ;
%%
void yyerror(char* s){

  fflush(stdout);
  fprintf(stderr,"\n%s\n",s);
}
int main( int argc, char *argv[] ){

  extern FILE *yyin;
  //++argv; −−argc;
  arg = argv[1];
  char name[30];
  strcpy(name, arg);
  strcat(name,".meupau");
  arg = name;
  //arg = argv[1];
  yyin = fopen( argv[1], "r");
  //yydebug = 1;
  //errors = 0;
  yyparse ();
}

Obs.: No arquivo em Yacc, ao invés da linha #include "y.tab.h" , será #include "c.tab.h"

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
KTachyon

Vou colocar um projecto nos downloads, mas enquanto ele é aprovado ou não, vou deixar-te aqui uma explicação muito básica.

Em primeiro lugar, devias normalizar coisas como:

prog: lista_cmd
  ;
lista_cmd: cmd
  |  lista_cmd cmd
  ;

Isto é exactamente o mesmo que teres apenas:

lista_cmd: cmd
  |  lista_cmd cmd
  ;

Precisamente porque prog é uma lista_cmd.

Neste pedaço de código:

%{
  #include <stdio.h>
  #include <string.h>
  #include "y.tab.h"
  void yyerror(char *);
  char* arg;
%}

podes fazer include a tudo o que quiseres e declarar todas as variáveis globais que tencionas utilizar dentro do ficheiro YACC. Uma dessas variáveis globais é uma estrutura que aponta para todo o teu código intermédio. A estrutura será qualquer coisa como:

typedef struct _a11 is_lista_cmd;

struct _a11 {
   is_cmd *exp;
   struct _a11 *next;
};

Como deves perceber, isto implica que exista uma estrutura is_cmd:

typedef struct _a10 is_cmd;

struct _a10 {
   cmd_type cmdType;
   union {
       is_decl_comp *decl_comp;
       is_cmd_expr *cmd_expr;
       is_cmd_comp *cmd_comp;
       is_sel *sel;
       is_loop *loop;
       is_func *func;
   } data_exp;
};

À parte das estruturas que tens que implementar na union, o cmdType pode ser um enum:

typedef enum {
   t_decl_comp,
   t_cmd_expr,
   t_cmd_comp,
   t_sel,
   t_loop,
   t_func
} cmd_type;

Por aí em diante.

Basicamente, para cada "linha" do teu YACC vais chamar código que cria estas estruturas com os valores e as devolve. A título de exemplo:

lista_cmd: cmd        { $$ = insert_lista_cmd($1, NULL); /* cria lista com "cmd" */ my_prog = $$; /* atribui a estrutura  a uma global */ }
  |  lista_cmd cmd   { $$ = insert_lista_cmd($2, $1); /* insere "cmd" na "lista_cmd" */ }
  ;

cmd: decl_comp { $$ = insert_decl_comp($1); }
  | cmd_expr  { $$ = insert_cmd_expr($1); }
  | cmd_comp  { $$ = insert_cmd_comp($1); }
  | sel       { $$ = insert_sel($1); }
  | loop      { $$ = insert_loop($1); }
  | func      { $$ = insert_func($1); }
  ;

Repara que o $$ aqui cai para um cmd que faz parte de uma lista_cmd, daí que estes sejam inseridos na lista principal encapsulados numa estrutura is_cmd como declarei lá atrás. Desta forma não tens conflitos de a estrutura que estás a inserir não ser a correcta (o que explica a razão de teres uma estrutura com um type e uma union com ponteiros para as diferentes estruturas que lá podem estar dentro).

Estas funções são definidas num ficheiro à parte e criam as estruturas que são devolvidas para o $$. Um exemplo para o de inserção de um cmd numa lista (ou seja, a função insert_lista_cmd()):

is_lista_cmd *insert_statement(is_cmd *exp, is_lista_cmd *next)
{
   is_lista_cmd *is = (is_lista_cmd*) malloc(sizeof(is_lista_cmd));

   is->exp = exp;

   if (next == NULL)
       return is;

   is_lista_cmd *aux;

   for (aux = next; aux->next != NULL; aux = aux->next);

   aux->next = is;

   return next;
}

Como podes ver, isto apenas recebe o cmd (que vem como argumento) e insere-os na lista_cmd que vem como argumento. O mesmo para as funções das variantes do cmd:

is_cmd *insert_loop(is_loop *loop)
{
   is_cmd *is_c = (is_cmd*) malloc(sizeof(is_cmd));

   is_c->cmdType = t_loop;
   is_c->data_exp.loop = loop;

   return is_c;
}

Por aí em diante.

EDIT: Para poderes ter uma visão mais abrangente do código, aconselho-te a veres o projecto que eu coloquei em:

https://www.portugal-a-programar.pt/topic/60028-cross-compiler-subset-de-lisp-para-c/

Editado por KTachyon

“There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.”

-- Tony Hoare

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!

Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.

Entrar Agora

×

Aviso Sobre Cookies

Ao usar este site você aceita os nossos Termos de Uso e Política de Privacidade. Este site usa cookies para disponibilizar funcionalidades personalizadas. Para mais informações visite esta página.