Bison/Flext 连接字符 *

Bison/Flext Concat Char *

我为我的新语言制作了一个 .l 和一个 .y 文件用于解析和计算结果:它工作正常!像这样的字符串:

SET(IFEL(MAJEQ(IFEL(EQ(VAL(16),MUL(VAL(2),VAL(8))),VAL(11),VAL(10)),VAL(10)),MUL(VAL(3),VAL(4)),SUB(VAL(6),VAL(2))))

被我的两个文件正确解析和计算:

%{
#include <stdio.h>
#include <string>
#include <cstring>
using namespace std;
extern int yylex();
extern void yyerror(char*);
%}

//Symbols
%union
{
    char *str_val;
    int int_val;
};

%token OPEN;
%token CLOSE;
%token SET;
%token STORE;
%token MUL;
%token ADD;
%token DIV;
%token SUB;
%token ABS;
%token IFEL;
%token AND;
%token OR;
%token NOT;
%token MAJEQ;
%token MINEQ;
%token MAJ;
%token MIN;
%token EQ;
%token GET;
%token S; /* separator */
%token VAR;
%token VAL;
%token <int_val>    NUMBER
%token <str_val>    IDENTIFIER

%type <int_val> Exp
%type <int_val> Cond
%type <int_val> Check
%type <int_val> Var

%start Expression

%%

Expression:
    /* empty */
    | SET OPEN Exp CLOSE
    {
        printf("value set %d\n",);
    }
    | STORE OPEN VAR OPEN IDENTIFIER CLOSE S Exp CLOSE
    {
        printf("var %s set on %d\n",,);
    }
    ;

Exp:
    Var
    | IFEL OPEN Cond S Exp S Exp CLOSE
    {
        if(==1){
            $$ = ;
        }else{
            $$ = ;
        }
    }
    | ADD OPEN Exp S Exp CLOSE
    {
        $$ = +;
    }
    | SUB OPEN Exp S Exp CLOSE
    {
        $$ = -;
    }
    | MUL OPEN Exp S Exp CLOSE
    {
        $$ = *;
    }
    | DIV OPEN Exp S Exp CLOSE
    {
        $$ = /; //TBD check div 0
    }
    | ABS OPEN Exp CLOSE
    {
        $$ = ; //TBD
    }
    ;

Cond:
    NOT OPEN Cond CLOSE
    {
        int result = ;
        if(result==1) $$ = 0;
        else $$ = 1;
    }
    | AND OPEN Cond S Cond CLOSE
    {
        int result1 = ;
        int result2 = ;
        if(result1==1 && result2==1) $$ = 1;
        else $$ = 0;
    }
    | OR OPEN Cond S Cond CLOSE
    {
        int result1 = ;
        int result2 = ;
        if(result1==1 || result2==1) $$ = 1;
        else $$ = 0;
    }
    | Check
    ;

Check:
    MAJ OPEN Exp S Exp CLOSE
    {
        int val1 = ;
        int val2 = ;
        if(val1>val2) $$ = 1;
        else $$ = 0;    
    }
    | MIN OPEN Exp S Exp CLOSE
    {
        int val1 = ;
        int val2 = ;
        if(val1<val2) $$ = 1;
        else $$ = 0;    
    }
    | EQ OPEN Exp S Exp CLOSE
    {
        int val1 = ;
        int val2 = ;
        if(val1==val2) $$ = 1;
        else $$ = 0;    
    }
    | MAJEQ OPEN Exp S Exp CLOSE
    {
        int val1 = ;
        int val2 = ;
        if(val1>=val2) $$ = 1;
        else $$ = 0;    
    }
    | MINEQ OPEN Exp S Exp CLOSE
    {
        int val1 = ;
        int val2 = ;
        if(val1<=val2) $$ = 1;
        else $$ = 0;    
    }
    ;

Var:
    VAR OPEN IDENTIFIER CLOSE
    {
        $$ = atoi(); //TBD 
    }
    | VAL OPEN NUMBER CLOSE
    {
        $$ = ;
    }
    | GET OPEN CLOSE
    {
        $$ = 11; //TBD 
    }
    ;
%%

%{
#include <string>
#include "expression.tab.h"
void yyerror(char*);
extern void printVars();
int yyparse(void);
%}

%%

[ \t\n]+    { /* ignore */ };

"("         return(OPEN);
")"         return(CLOSE);
"SET"       return(SET);
"STORE"     return(STORE);
"MUL"       return(MUL);
"ADD"       return(ADD);
"DIV"       return(DIV);
"SUB"       return(SUB);
"ABS"       return(ABS);
"IFEL"      return(IFEL);
"NOT"       return(NOT);
"AND"       return(AND);
"OR"        return(OR);
"MAJEQ"     return(MAJEQ);
"MINEQ"     return(MINEQ);
"MAJ"       return(MAJ);
"MIN"       return(MIN);
"EQ"        return(EQ);
"VAR"       return(VAR);
"VAL"       return(VAL);
"GET"       return(GET);
","         return(S);

[[:digit:]]+  { yylval.int_val = atoi(yytext);   return NUMBER;}
[[:alnum:]]+  { yylval.str_val = strdup(yytext); return IDENTIFIER;}

.           return yytext[0];

%%

void yyerror(char *s){ 
    printf("<ERR> %s at %s in this line:\n", s, yytext);
    }

int yywrap (void){
    }

int main(int num_args, char** args){
    if(num_args != 2) {printf("usage: ./parser filename\n"); exit(0);}
    FILE* file = fopen(args[1],"r");
    if(file == NULL) {printf("couldn't open %s\n",args[1]); exit(0);}
    yyin = file;
    yyparse();
    fclose(file);
}

但实际上如您所见,input 中 Var 中的值不会是静态的,而应该是动态的。所以我的下一步是修改项目:而不是计算应该写一个C++代码以使计算动态化。

我的问题: 1)你有更好的解决方案而不是将每一步都连接成一个 char * 来制作代码吗? 2)如果没有,你能帮我找到一个聪明的方法来连接所有的字符串并解决我在编译时遇到的以下错误:

expression.y:75:43: error: invalid operands of types ‘const char [2]’ and ‘char*’ to binary ‘operator+’ $$ = "("++"-"++")";

...我不想每次都使用"malloc"...

char* str;
str = malloc(1+strlen(text1)+strlen(text2));
strcpy(str, text1);
strcat(str, text2);

有什么更聪明的方法吗?以下flex和bison修改文件:

expression.l

%{
#include <string>
#include "expression.tab.h"
void yyerror(char*);
extern void printVars();
int yyparse(void);
%}

%%

[ \t\n]+    { /* ignore */ };

"("         return(OPEN);
")"         return(CLOSE);
"SET"       return(SET);
"STORE"     return(STORE);
"MUL"       return(MUL);
"ADD"       return(ADD);
"DIV"       return(DIV);
"SUB"       return(SUB);
"ABS"       return(ABS);
"IFEL"      return(IFEL);
"NOT"       return(NOT);
"AND"       return(AND);
"OR"        return(OR);
"MAJEQ"     return(MAJEQ);
"MINEQ"     return(MINEQ);
"MAJ"       return(MAJ);
"MIN"       return(MIN);
"EQ"        return(EQ);
"VAR"       return(VAR);
"VAL"       return(VAL);
"GET"       return(GET);
","         return(S);

([a-z0-9]+)|([0-9]+.[0-9]+)     { yylval.str_val = strdup(yytext); return IDENTIFIER;}

.           return yytext[0];

%%

void yyerror(char *s){ 
    printf("<ERR> %s at %s in this line:\n", s, yytext);
    }

int yywrap (void){
    }

int main(int num_args, char** args){
    if(num_args != 2) {printf("usage: ./parser filename\n"); exit(0);}
    FILE* file = fopen(args[1],"r");
    if(file == NULL) {printf("couldn't open %s\n",args[1]); exit(0);}
    yyin = file;
    yyparse();
    fclose(file);
}

expression.y

%{
#include <stdio.h>
#include <string>
#include <cstring>
using namespace std;
extern int yylex();
extern void yyerror(char*);
%}

//Symbols
%union
{
    char *str_val;
    int int_val;
};

%token OPEN;
%token CLOSE;
%token SET;
%token STORE;
%token MUL;
%token ADD;
%token DIV;
%token SUB;
%token ABS;
%token IFEL;
%token AND;
%token OR;
%token NOT;
%token MAJEQ;
%token MINEQ;
%token MAJ;
%token MIN;
%token EQ;
%token GET;
%token S; /* separator */
%token VAR;
%token VAL;

%token <str_val> IDENTIFIER

%type <str_val> Exp
%type <str_val> Cond
%type <str_val> Check
%type <str_val> Var

%start Expression

%%

Expression:
    /* empty */
    | SET OPEN Exp CLOSE
    {
        printf("%s\n",);
    }
    | STORE OPEN VAR OPEN IDENTIFIER CLOSE S Exp CLOSE
    {
        printf("var %s with following code:\n%s\n",,);
    }
    ;

Exp:
    Var
    | IFEL OPEN Cond S Exp S Exp CLOSE
    {
        $$ = "("++" == 'true') ? ("++") : ("++")";
    }
    | ADD OPEN Exp S Exp CLOSE
    {
        $$ = "("++"+"++")"; 
    }
    | SUB OPEN Exp S Exp CLOSE
    {
        $$ = "("++"-"++")"; 
    }
    | MUL OPEN Exp S Exp CLOSE
    {
        $$ = "("++"*"++")"; 
    }
    | DIV OPEN Exp S Exp CLOSE
    {
        $$ = "("++"/"++")"; //TBD check div 0
    }
    | ABS OPEN Exp CLOSE
    {
        $$ = "("++">0) ? "++" : "(++"*(-1))";
    }
    ;

Cond:
    NOT OPEN Cond CLOSE
    {
        $$ = "("++"=='true') ? 'false' : 'true'";
    }
    | AND OPEN Cond S Cond CLOSE
    {
        $$ = "("++"=='true' && "++"=='true') ? 'true' : 'false'";
    }
    | OR OPEN Cond S Cond CLOSE
    {
        $$ = "("++"=='true' || "++"=='true') ? 'true' : 'false'";
    }
    | Check
    ;

Check:
    MAJ OPEN Exp S Exp CLOSE
    {
        $$ = "("++">"++") ? 'true' : 'false'";
    }
    | MIN OPEN Exp S Exp CLOSE
    {
        $$ = "("++"<"++") ? 'true' : 'false'";  
    }
    | EQ OPEN Exp S Exp CLOSE
    {
        $$ = "("++"=="++") ? 'true' : 'false'"; 
    }
    | MAJEQ OPEN Exp S Exp CLOSE
    {
        $$ = "("++">="++") ? 'true' : 'false'";
    }
    | MINEQ OPEN Exp S Exp CLOSE
    {
        $$ = "("++"<="++") ? 'true' : 'false'";
    }
    ;

Var:
    VAR OPEN IDENTIFIER CLOSE
    {
        //TBD check if variable exists in the engine
        $$ = ;
    }
    | VAL OPEN IDENTIFIER CLOSE
    {
        //TBD check correct value
        $$ = ;
    }
    | GET OPEN CLOSE
    {
        $$ = "getField()"; //TBD to implement in the engine
    }
    ;
%%

嗯...我是这样解决问题的:

...
Exp:
    Var
    | IFEL OPEN Cond S Exp S Exp CLOSE
    {
        string t1 = ;
        string t2 = ;
        string t3 = ;
        string result = "("+t1+" == 'true') ? ("+t2+") : ("+t3+")";
        $$ = (char*)result.c_str();
    }
...

工作正常...

如果没有某种形式的内存分配,很难进行字符串连接。当然,避免避免 malloc 是可能的——你可以使用 new 来代替,或者将内存分配隐藏在 std::stringstd::stringstream 中——但最后,您将不得不处理动态内存分配,并且在不再需要时释放内存。

值得注意的是,您在 IDENTIFIER 的扫描器操作中(正确)使用 strdup 是内存泄漏,因为您从未释放分配的内存。所以你已经需要处理这个问题了。

正如您所注意到的,在 C 中进行字符串连接可能非常笨拙。在这种情况下,减少笨拙是值得的。我的首选解决方案是我的包装函数 concatf,其原型与 printf 类似,只是它 returns 是一个 malloc 字符串而不是打印。 (有关各种平台上的实施,请参阅 )。

借助这个函数,可以写成:

Exp:
    Var
    | IFEL OPEN Cond S Exp S Exp CLOSE
    {
        $$ = concatf("(%s == 'true') ? (%s) : (%s)", , , );
    }

请注意 x == 'true' 不是有效的 C++。您的意思可能是 == true,但这是一个危险的习语;更好的是显式转换为 bool (尽管在三元运算符的上下文中这实际上是多余的),所以我认为您实际上想要

        $$ = concatf("bool(%s) ? (%s) : (%s)", , , );

或者只是

        $$ = concatf("(%s) ? (%s) : (%s)", , , );

但是,如上所述,这会导致内存泄漏,因为 malloc 的字符串永远不会被释放。所以让我们解决这个问题。首先,在每个动作中,有必要显式释放所有不再使用的 malloc 值。在像您这样的简单情况下,这将是所有 malloc 的值,除了 malloc 的值只是分配给不同的非终端的单元产品。由于所有 IDENTIFIER 都具有由 strdup 创建的语义值,因此可以合理地假设所有 str_val 值都已分配(这需要是一个约束;如果您曾经创建 str_val 值来自文字字符串,你最终会遇到问题)。现在,我们可以编写规则:

Exp:
    Var  { /* No free needed; this is a unit production */ }
    | IFEL OPEN Cond S Exp S Exp CLOSE
    {
        $$ = concatf("(%s) ? (%s) : (%s)", , , );
        free(); free(); free();
    }

再举个例子。请注意在最后一条规则中添加的 strdup

Var:
    VAR OPEN IDENTIFIER CLOSE
    {
        $$ = ; /* No free needed; value is moved on the stack */
    }
    | VAL OPEN IDENTIFIER CLOSE
    {
        $$ = ; /* As above */
    }
    | GET OPEN CLOSE
    {
        $$ = strdup("getField()"); /* str_val's must be malloc'd */
    }
    ;

(除了在文字上调用 strdup 之外还有其他方法,但通常用例不常见,开销很小。)

该样式将处理执行规则操作的所有情况,但有时 bison 会在不每次调用规则的情况下从堆栈中丢弃值。这将在错误恢复期间发生,并且在解析器堆栈非空时在不成功的解析结束时发生。为了帮助处理这种情况,bison 允许您声明一个 destructor action,它将在它丢弃的每个堆栈值上调用。在这种情况下,声明几乎是微不足道的:

%destructor { free($$); } <str_val>