Как сгенерировать код с помощью flex / bison и поместить его в файл - PullRequest
0 голосов
/ 06 сентября 2018

Я пишу переводчик для проекта uni, который должен переводить данный код на Паскале в код на ассемблере с помощью flex / bison. Я написал парсер и лексер, который генерирует таблицу символов (атм работает правильно только без процедур и функций). И мой вопрос: как мне сгенерировать из него ассемблерный код и распечатать его в файл.

Вот мой лексер:

%{
#include "parser.tab.h"
#include <string.h>
#define YY_FLEX_DEBUG 1
%}

letter      [a-zA-Z]
digit       [0-9]
ID          {letter}({letter}|{digit})*
delim       [ \t\n]
NUM         {digit}+(\.{digit}+)?(E[+\-]?(digit)+)?
ws          {delim}+

%%
{ws}        {                                           }
if          {return(IF);                                }
then        {return(THEN);                              }
else        {return(ELSE);                              }
{NUM}       {yylval.stringValue = strdup(yytext); return(NUM);          }
"<"         {yylval.stringValue = "<"; return(RELOP);   }
"<="        {yylval.stringValue = "<="; return(RELOP);  }
"="         {yylval.stringValue = "="; return(RELOP);   }
">"         {yylval.stringValue = ">"; return(RELOP);   }
">="        {yylval.stringValue = ">="; return(RELOP);  }
"<>"        {yylval.stringValue = "<>"; return(RELOP);  }
":="        {return(ASSIGNOP);                          }
do          {return(DO);                                }
program     {return(PROGRAM);                           }
var         {return(VAR);                               }
array       {return(ARRAY);                             }
of          {return(OF);                                }
integer     {return(INTEGER);                           }
real        {return(REAL);                              }
function    {return(FUNCTION);                          }
procedure   {return(PROCEDURE);                         }
begin       {return(START);                             }
end         {return(END);                               }
div         {yylval.stringValue = "div"; return(MULOP); }
mod         {yylval.stringValue = "mod"; return(MULOP); }
and         {yylval.stringValue = "and"; return(MULOP); }
"*"         {yylval.stringValue = "*"; return(MULOP);   }
"/"         {yylval.stringValue = "/"; return(MULOP);   }
while       {return(WHILE);                             }
or          {return(OR);                                }
"+"         {yylval.stringValue = "+"; return(SIGN);    }
"-"         {yylval.stringValue = "-"; return(SIGN);    }
".."        {return(DOUBLEDOT);                         }
","         {return *yytext;                            }
"("         {return *yytext;                            }
")"         {return *yytext;                            }
"["         {return *yytext;                    }
"]"         {return *yytext;                    }
";"         {return *yytext;                                }
":"         {return *yytext;                                }
"."         {return *yytext;                                }
not         {return(NOT);                               }
{ID}        {yylval.stringValue= strdup(yytext); return(ID);}
%%
int yywrap(void){}

Вот мой парсер:

%{
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include "SymbolTable.h"
    int errors;
    int lable;
    #define YYDEBUG 1

    install (char *sym_name)
    {
        symrec *s;
        s = getsym(sym_name);
        if (s == 0)
            s = putsym(sym_name);
        else {
            errors++;
            printf("%s is defined\n", sym_name);
        }
    }

    install_num (char *sym_name)
    {
        symrec *s;
        s = getsym(sym_name);
        if (s == 0)
            s = putnum(sym_name);
    }

    context_check(char *sym_name)
    {
        if (getsym(sym_name) == 0)
            printf("%s is undeclared\n", sym_name);
    }
%}
%union
{
    int intValue;
    float floatValue;
    char *stringValue;
    int adress;
}
%start program
%token <stringValue> ID
%token <stringValue> NUM
%token IF THEN PROGRAM VAR ARRAY
%token OF INTEGER REAL
%token FUNCTION PROCEDURE
%token START END
%token ASSIGNOP RELOP MULOP
%token ELSE WHILE DO
%token SIGN OR
%token DOUBLEDOT
%token NOT
%left '-' '+'
%left '*' '/'
%%
program: PROGRAM ID '(' prog_list ')' ';' declarations subprogram_declarations compound_statement '.'
         ;
prog_list: ID
         | prog_list ',' ID
         ;
identifier_list: ID  {install($1);}
         | identifier_list ',' ID {install($3);} 
         ;
declarations: declarations VAR identifier_list ':' type ';'
         | /* empty */
         ;
type: standart_type
         | ARRAY '[' NUM DOUBLEDOT NUM ']' OF REAL {set_type("REALARR");}
         | ARRAY '[' NUM DOUBLEDOT NUM ']' OF INTEGER {set_type("INTARR");}
         ;
standart_type: INTEGER {set_type("INTEGER");}
         | REAL {set_type("REAL");}
         ;
subprogram_declarations: subprogram_declarations subprogram_declaration ';'
         | /* empty */
;
subprogram_declaration: subprogram_head declarations compound_statement;
subprogram_head: FUNCTION ID arguments ':' INTEGER ';' {install($2); set_type("INTEGER");}
         | FUNCTION ID arguments ':' REAL ';' {install($2); set_type("REAL");}
         | PROCEDURE ID arguments ';' {install($2); set_proc($2);}
         ;
arguments: '(' parameter_list ')'
         | /* empty */;
parameter_list: identifier_list ':' type
         | parameter_list ';' identifier_list ':' type
         ;
compound_statement: START
                    optional_statements END
         ;
optional_statements: statement_list
         | /* empty */
         ;
statement_list: statement
         | statement_list ';' statement
         ;
statement: variable ASSIGNOP expression
         | procedure_statement
         | compound_statement
         | IF expression THEN statement ELSE statement
         | WHILE expression DO statement
         ;
variable: ID {context_check($1);}
         | ID '[' expression ']' {context_check($1);}
         ;
procedure_statement: ID 
         | ID '(' expression_list ')'
         ;
expression_list: expression
         | expression_list ',' expression
         ;
expression: simple_expression
         | simple_expression RELOP simple_expression
         ;
simple_expression: term
         | SIGN term
         | simple_expression SIGN term
         | simple_expression OR term
         ;
term: factor
         | term MULOP factor
         ;
factor: variable
         | ID '(' expression_list ')' {context_check($1);}
         | NUM {install_num($1);}
         | '(' expression ')'
         | NOT factor
         ;
%%
main (int argc, char *argv[]) {
    FILE *output = fopen("output.asm", "w");
    fprintf(output, "\t  jump.i #lab0\n");
    extern FILE *yyin;
    ++argv; --argc;
    yyin = fopen(argv[0], "r");
    yydebug = 1;
    errors = 0;
    yyparse();
    print_sym_table();
    fprintf(output, "\t  exit");
    fclose(output);

}
yyerror (char *s) /* Called by yyparse on error */
{
    errors++;
    printf ("%s\n", s);
}

Вот таблица символов:

struct symrec
{
    char *name;
    int addr;
    char *type;
    struct symrec *next; 
};
typedef struct symrec symrec;
symrec *sym_table = (symrec *)0;
symrec *putsym();
symrec *getsym();
symrec *putnum();
void set_type();
void set_proc();
void set_func();
void print_sym_table();

symrec *putsym(char *sym_name)
{
    symrec *ptr;
    ptr = (symrec *)malloc(sizeof(symrec));
    ptr->name = (char *)malloc(strlen(sym_name) + 1);
    ptr->type = NULL;
    strcpy(ptr->name,sym_name);
    ptr->next = (struct symrec *)sym_table;
    sym_table = ptr;
    return ptr;
}

symrec *putnum(char *sym_name)
{
    symrec *ptr;
    char *dPos = strchr(sym_name, '.');
    char *ePos = strchr(sym_name, 'e');
    ptr = (symrec *)malloc(sizeof(symrec));
    ptr->name = (char *)malloc(strlen(sym_name) + 1);
    if ((dPos == NULL) && (ePos == NULL)){
        ptr->type = (char *)malloc(strlen("INTEGER") + 1);
        strcpy(ptr->type, "INTEGER");
    }
    else if ((dPos != NULL) && (ePos == NULL)) {
        ptr->type = (char *)malloc(strlen("REAL") + 1);
        strcpy(ptr->type, "REAL");
    }
    else {
        ptr->type = (char *)malloc(strlen("FLOAT") + 1);
        strcpy(ptr->type, "FLOAT");
    }
    strcpy(ptr->name,sym_name);
    ptr->next = (struct symrec *)sym_table;
    sym_table = ptr;
    return ptr;
}

void set_type(char *type)
{
    symrec *ptr;
    for (ptr = sym_table; ptr != (symrec *)0; ptr = (symrec *)ptr->next) {
        if (ptr->type == NULL) {
            ptr->type = (char *)malloc(strlen(type) + 1);
            strcpy(ptr->type, type);
        }
    }
}

void set_proc(char *sym_name) {
    symrec *ptr;
    for (ptr = sym_table; ptr != (symrec *)0; ptr = (symrec *)ptr->next)
        if (strcmp (ptr->name, sym_name) == 0){
            ptr->type = (char *)malloc(strlen("PROC") + 1);
            strcpy(ptr->type, "PROC");
        }
}

symrec *getsym(char *sym_name)
{
    symrec *ptr;
    for (ptr = sym_table; ptr != (symrec *)0; ptr = (symrec *)ptr->next)
        if (strcmp (ptr->name, sym_name) == 0)
            return ptr;
    return 0;
}

void print_sym_table()
{
    symrec *ptr;
    for (ptr = sym_table; ptr != (symrec *)0; ptr = (symrec *)ptr->next)
        printf("\n%s    %s\n", ptr->name, ptr->type);
}

Простой тестовый файл

program example(input, output);
var x, y: integer;
var g,h:real;

begin
  g:=x+y;
  write(g)
end.

А что он должен выводить в выходной файл:

     jump.i  #lab0                   ;jump.i  lab0
lab0:
        add.i   0,4,24                  ;add.i   x,y,$t0
        inttoreal.i 24,28               ;inttoreal.i $t0,$t1
        mov.r   28,8                    ;mov.r   $t1,g
        write.r 8                       ;write.r g
        exit                            ;exit    

комментарии (; jump.i lab0) не нужны.

Я знаю, как следует вычислять адреса переменных, и я могу перевести Паскаль-код на этот ассемблер на бумаге, но я действительно не понимаю, где и что я должен поместить в файл Bison или Flex, чтобы он генерировал код на ассемблере в вывод файл. Я попытался сгенерировать метки для операторов начала в правиле:

compound_statement: START {fprintf(output, "lab0\n");}
                    optional_statements END

Но произошла ошибка сегментации. Совершенно очевидно, как генерировать метки, но как мне генерировать

add.i 0, 4, 24

Должен ли я создать другой парсер после того, как я построю таблицу символов с этим? Или это выполнимо без дополнительного парсера. Нужно несколько советов, что делать дальше.

Ответы [ 2 ]

0 голосов
/ 06 сентября 2018

Например, в вашем парсере есть шаблон:

| term MULOP factor

Вы хотели бы применить к этому шаблону что-то вроде:

{ fprintf(output, "mul term, factor, result\n"); }

но он начинает очень быстро залипать: где термин, фактор и куда вы должны поместить результат? Самый простой ответ - это стек: всякий раз, когда на переменную ссылаются, помещают ее значение в стек. всякий раз, когда выполняется операция, вставьте операнд (ы) в регистры, выполните операцию и отправьте результат, так что вышеприведенное становится:

    {
   fprintf(output, "pop r0; pop r1; mul r1, r0, r0;");
   fprintf(output, "push r0\n");
}

и присваивания просто вставляют стек в переменную.

0 голосов
/ 06 сентября 2018

Итак, у вас есть этот бит кода:

compound_statement: START {fprintf(output, "lab0\n");}
                    optional_statements END

Вы идете по правильному пути, делая это таким образом, но вы получаете ошибку сегментации, когда добавляете ее, и это потому, что output не инициализирован.

Я не вижу, где вы объявили output, на который там ссылаются, но это не то же самое, что объявлено в main, где вы открываете файл для вывода.

main (int argc, char *argv[]) {
    FILE *output = fopen("output.asm", "w");

Эта версия output является локальной для main и видима только внутри этой функции. Если вы удалите объявление output из main и оставите только присвоение, вы будете присваивать результаты fopen глобально объявленной версии output, которую использует ваш код бизона.

main (int argc, char *argv[]) {
    output = fopen("output.asm", "w");

Не знаю, почему у вас возникла путаница с другой частью вашего вопроса, поскольку вы продемонстрировали, как это сделать, уже в вашем анализаторе. Возьми этот бит своего парсера:

variable: ID {context_check($1);}

Он принимает значение "ID" - $1 - и передает его этой функции. Если вы хотите, чтобы переменная содержала значение, вы бы сохранили его в $$. Затем, когда вы используете «переменную» выше, как здесь:

statement: variable ASSIGNOP expression

$1 будет содержать любое значение, которое вы указали в $$ для «переменной». $2 будет значением, полученным из токена "ASSIGNOP", а $3 будет иметь результаты из "выражения". И снова, если вы сохраните значение в $$, вы сможете использовать его во всем, что ожидает «утверждение».

$$, $1 и т. Д. - это все типы, которые вы создали с помощью %union, поэтому вы также можете сделать $$.intValue или $2.stringValue, если вам нужно указать, какое значение вы Настраиваем.

...