我为我的新语言创建了一个.l和一个.y文件,用于解析和计算结果:它运行得很好!像这样的绳子:
集合(IFEL(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",$3);
}
| STORE OPEN VAR OPEN IDENTIFIER CLOSE S Exp CLOSE
{
printf("var %s set on %d\n",$5,$8);
}
;
Exp:
Var
| IFEL OPEN Cond S Exp S Exp CLOSE
{
if($3==1){
$$ = $5;
}else{
$$ = $7;
}
}
| ADD OPEN Exp S Exp CLOSE
{
$$ = $3+$5;
}
| SUB OPEN Exp S Exp CLOSE
{
$$ = $3-$5;
}
| MUL OPEN Exp S Exp CLOSE
{
$$ = $3*$5;
}
| DIV OPEN Exp S Exp CLOSE
{
$$ = $3/$5; //TBD check div 0
}
| ABS OPEN Exp CLOSE
{
$$ = $3; //TBD
}
;
Cond:
NOT OPEN Cond CLOSE
{
int result = $3;
if(result==1) $$ = 0;
else $$ = 1;
}
| AND OPEN Cond S Cond CLOSE
{
int result1 = $3;
int result2 = $5;
if(result1==1 && result2==1) $$ = 1;
else $$ = 0;
}
| OR OPEN Cond S Cond CLOSE
{
int result1 = $3;
int result2 = $5;
if(result1==1 || result2==1) $$ = 1;
else $$ = 0;
}
| Check
;
Check:
MAJ OPEN Exp S Exp CLOSE
{
int val1 = $3;
int val2 = $5;
if(val1>val2) $$ = 1;
else $$ = 0;
}
| MIN OPEN Exp S Exp CLOSE
{
int val1 = $3;
int val2 = $5;
if(val1<val2) $$ = 1;
else $$ = 0;
}
| EQ OPEN Exp S Exp CLOSE
{
int val1 = $3;
int val2 = $5;
if(val1==val2) $$ = 1;
else $$ = 0;
}
| MAJEQ OPEN Exp S Exp CLOSE
{
int val1 = $3;
int val2 = $5;
if(val1>=val2) $$ = 1;
else $$ = 0;
}
| MINEQ OPEN Exp S Exp CLOSE
{
int val1 = $3;
int val2 = $5;
if(val1<=val2) $$ = 1;
else $$ = 0;
}
;
Var:
VAR OPEN IDENTIFIER CLOSE
{
$$ = atoi($3); //TBD
}
| VAL OPEN NUMBER CLOSE
{
$$ = $3;
}
| 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);
}但实际上,您可以看到,输入中的Var中的值不是静态的,而是动态的。因此,我的下一步是修改项目:而不是编写一个C++代码,以便使计算变得动态。
我的问题是:( 1)您是否有更好的解决方案,而不是将每个步骤串联为char *来编写代码? 2)如果没有,您能帮助我找到一种聪明的方法来连接所有字符串并解决我在编译过程中面临的以下错误:
expression.y:75:43:错误:类型‘const 2’和‘char *’到二进制‘operator+’$$ =(“+$3+”-“+$5+”)的操作数无效;
..。我不想每次都用"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",$3);
}
| STORE OPEN VAR OPEN IDENTIFIER CLOSE S Exp CLOSE
{
printf("var %s with following code:\n%s\n",$5,$8);
}
;
Exp:
Var
| IFEL OPEN Cond S Exp S Exp CLOSE
{
$$ = "("+$3+" == 'true') ? ("+$5+") : ("+$7+")";
}
| ADD OPEN Exp S Exp CLOSE
{
$$ = "("+$3+"+"+$5+")";
}
| SUB OPEN Exp S Exp CLOSE
{
$$ = "("+$3+"-"+$5+")";
}
| MUL OPEN Exp S Exp CLOSE
{
$$ = "("+$3+"*"+$5+")";
}
| DIV OPEN Exp S Exp CLOSE
{
$$ = "("+$3+"/"+$5+")"; //TBD check div 0
}
| ABS OPEN Exp CLOSE
{
$$ = "("+$3+">0) ? "+$3+" : "(+$3+"*(-1))";
}
;
Cond:
NOT OPEN Cond CLOSE
{
$$ = "("+$3+"=='true') ? 'false' : 'true'";
}
| AND OPEN Cond S Cond CLOSE
{
$$ = "("+$3+"=='true' && "+$5+"=='true') ? 'true' : 'false'";
}
| OR OPEN Cond S Cond CLOSE
{
$$ = "("+$3+"=='true' || "+$5+"=='true') ? 'true' : 'false'";
}
| Check
;
Check:
MAJ OPEN Exp S Exp CLOSE
{
$$ = "("+$3+">"+$5+") ? 'true' : 'false'";
}
| MIN OPEN Exp S Exp CLOSE
{
$$ = "("+$3+"<"+$5+") ? 'true' : 'false'";
}
| EQ OPEN Exp S Exp CLOSE
{
$$ = "("+$3+"=="+$5+") ? 'true' : 'false'";
}
| MAJEQ OPEN Exp S Exp CLOSE
{
$$ = "("+$3+">="+$5+") ? 'true' : 'false'";
}
| MINEQ OPEN Exp S Exp CLOSE
{
$$ = "("+$3+"<="+$5+") ? 'true' : 'false'";
}
;
Var:
VAR OPEN IDENTIFIER CLOSE
{
//TBD check if variable exists in the engine
$$ = $3;
}
| VAL OPEN IDENTIFIER CLOSE
{
//TBD check correct value
$$ = $3;
}
| GET OPEN CLOSE
{
$$ = "getField()"; //TBD to implement in the engine
}
;
%%发布于 2015-08-07 12:54:24
好吧..。我用这样的方式解决了这个问题:
...
Exp:
Var
| IFEL OPEN Cond S Exp S Exp CLOSE
{
string t1 = $3;
string t2 = $5;
string t3 = $7;
string result = "("+t1+" == 'true') ? ("+t2+") : ("+t3+")";
$$ = (char*)result.c_str();
}
...效果很好..。
发布于 2015-08-07 15:25:28
如果没有某种形式的内存分配,就很难进行字符串连接。当然,可以避免使用malloc --您可以使用new,或者将内存分配隐藏在std::string或std::stringstream中--但最终,您将不得不处理动态内存分配,以及在不再需要时释放内存。
值得注意的是,在扫描仪操作中(正确)使用strdup用于IDENTIFIER是内存泄漏,因为您从未释放分配的内存。所以你已经需要处理这个问题了。
正如您注意到的,在C中进行字符串连接可能非常笨拙。在这种情况下,减少笨重是值得的。我喜欢的解决方案是我的包装函数concatf,它的原型就像printf,只是它返回一个malloc的字符串而不是打印。(有关各种平台上的实现,请参见这个答案 )。
在这一功能的帮助下,可以编写:
Exp:
Var
| IFEL OPEN Cond S Exp S Exp CLOSE
{
$$ = concatf("(%s == 'true') ? (%s) : (%s)", $3, $5, $7);
}注意,x == 'true'不是有效的C++。您可能是指== true,但这是一个危险的成语;更好的方法是将其显式转换为bool (尽管在三元运算符的上下文中这实际上是多余的),所以我认为您实际上希望
$$ = concatf("bool(%s) ? (%s) : (%s)", $3, $5, $7);或者只是
$$ = concatf("(%s) ? (%s) : (%s)", $3, $5, $7);但是,如上所述,这会导致内存泄漏,因为malloc的字符串从未被释放。所以让我们来解决这个问题。首先,在每个操作中,必须显式释放所有不再使用的malloc'd值。在像您这样的简单情况下,这将是所有malloc'd值,但单元产品除外,其中malloc'd值只是分配给不同的非终端。因为所有IDENTIFIER都有由strdup创建的语义值,所以有理由假定所有str_val值都是malloc的(这需要是一个约束;如果您曾经从一个文字字符串创建str_val值,那么最终会出现问题)。现在,我们可以写一条规则:
Exp:
Var { /* No free needed; this is a unit production */ }
| IFEL OPEN Cond S Exp S Exp CLOSE
{
$$ = concatf("(%s) ? (%s) : (%s)", $3, $5, $7);
free($3); free($5); free($7);
}另一个例子。注意最后一条规则中添加的strdup。
Var:
VAR OPEN IDENTIFIER CLOSE
{
$$ = $3; /* No free needed; value is moved on the stack */
}
| VAL OPEN IDENTIFIER CLOSE
{
$$ = $3; /* As above */
}
| GET OPEN CLOSE
{
$$ = strdup("getField()"); /* str_val's must be malloc'd */
}
;(除了对文本调用strdup之外,还有其他替代方法,但通常用例并不常见,开销也很小。)
该样式将处理执行规则操作的所有情况,但也会在不每次调用规则的情况下丢弃堆栈中的值。这将发生在错误恢复期间,以及解析器堆栈非空时不成功的解析结束时。为了在这种情况下提供帮助,bison允许您声明一个破坏作用,它将在它丢弃的每个堆栈值上调用。在这种情况下,声明几乎是微不足道的:
%destructor { free($$); } <str_val>https://stackoverflow.com/questions/31877704
复制相似问题