如何使用 flex/bison 生成代码并将其放入文件

How do I generate code with flex/bison and put it to file

我正在为 uni 项目编写一个翻译器,它应该使用 flex/bison 将给定的 Pascal 代码翻译成汇编代码。我已经编写了解析器和词法分析器,它们生成符号 table(atm 只有在没有过程和函数的情况下才能正常工作)。我的问题是,如何从中生成汇编代码并将其打印到文件中。

这是我的词法分析器:

%{
#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();}
         | identifier_list ',' ID {install();} 
         ;
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(); set_type("INTEGER");}
         | FUNCTION ID arguments ':' REAL ';' {install(); set_type("REAL");}
         | PROCEDURE ID arguments ';' {install(); set_proc();}
         ;
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();}
         | ID '[' expression ']' {context_check();}
         ;
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();}
         | NUM {install_num();}
         | '(' 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);
}

这里是符号table:

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) 不是必需的。

我知道应该如何计算变量的地址并且我可以在纸上将 Pascal 代码翻译成这个汇编程序,但我真的不明白我应该把什么放在 bison 或 flex 文件中以便生成汇编代码到输出文件中。我尝试为规则中的开始语句生成标签:

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

但是出现分段错误。很明显如何生成标签,但我应该如何生成

add.i 0, 4, 24

我是否应该在用这个解析器构建符号 table 后创建另一个解析器?或者在没有额外解析器的情况下是否可行。需要一些提示下一步该怎么做。

所以你得到了这段代码:

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

您这样做是正确的,但是当您添加它时会出现分段错误,这是因为 output 未初始化。

我看不到您在哪里声明了那里引用的 output,但它与您在其中打开文件的 main 中声明的不同输出。

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

该版本 outputmain 的本地版本,并且仅在该函数内部可见。如果您从 main 中删除 output 的声明并只保留分配,您将把 fopen 的结果分配给您的 bison 的 output 的全局声明版本代码正在使用。

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

不确定为什么您对问题的另一部分感到困惑,因为您已经在解析器中演示了如何做到这一点。拿下你的解析器:

variable: ID {context_check();}

它获取 "ID" 的值 - </code> - 并将其传递给该函数。如果您希望 "variable" 包含一个值,您可以将其存储在 <code>$$ 中。然后当你像这里一样使用 "variable" 更高的时候:

statement: variable ASSIGNOP expression

</code> 将包含您为 "variable" 输入的任何值 <code>$$</code> 将是从 "ASSIGNOP" 令牌获得的值,而 <code> 将具有从 "expression" 获得的结果。同样,如果您在 $$ 中存储一个值,您就可以在任何需要 "statement".

的地方使用它

$$</code>等都是你用<code>%union创建的类型,所以你也可以做$$.intValue.stringValue 如果您需要具体说明您设置的值。

例如,在您的解析器中,您有一个模式:

| 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");
}

赋值只是将堆栈弹出到一个变量中。