本文没有真的实现规则引擎,还是跟linux一样stdin输入stdout输出,没有集成到库中。但是还是一个不错的示例。
这可能是我写的学习Flex&Bison的最后一个版本了。
本篇文章的代码基于原版文档的中的——source code for calculator代码
https://www.epaperpress.com/lexandyacc/index.html
我改造的程序,由四个部分组成,分别是calc3.l、calc3.y、calc3.h、calc3.c。
编译命令
flex *.l
bison -d *.y
g++ -o cal *.c -lfl
支持的操作
采用类C语言设计,支持任意定义变量名字,支持整型、浮点型、bool型以及简易容器类型,打印变量或者常量必须使用print打印,支持if else、while操作、支持逻辑的&&、||、^、!操作,以及+-*/,还有==、>=、<=。一行语句结束必须加;。
已知bug
单个if语句(后面不写else)需要跟两个;结尾。
操作举例
a=3;b=4;if(a>b){a++;print a;}else {print b;}
11 add al;11 in al;
a=true;if(a)print 99.1;;
if(11>=22)print 33;else print 22;
a=3*4+5;print a;
a=true;b=false;if(a&&!b)print 99;;
a=true;b=false;if(a^b)print 99;;
源代码
calc3.l
%{
#include <stdlib.h>
#include "calc3.h"
#include "calc3.tab.h"
void yyerror(const char *);
#include <arpa/inet.h>
%}
%%
"true" {
yylval.iValue = 1;
return INTEGER;
}
"false" {
yylval.iValue = 0;
return INTEGER;
}
[0-9]*\.[0-9]+ {
yylval.flo = atof(yytext);
return FLO;
}
[1-9][0-9]* {
yylval.iValue = atoi(yytext);
return INTEGER;
}
[-()<>=+*/;{}.!^] {
return *yytext;
}
">=" return GE;
"<=" return LE;
"==" return EQ;
"!=" return NE;
"while" return WHILE;
"if" return IF;
"else" return ELSE;
"print" return PRINT;
"++" return GG;
"--" return LL;
"&&" return AND;
"||" return OR;
"add" return AD;
"in" return IN;
[a-zA-Z][A-Za-z0-9]* {yylval.sIndex = strdup(yytext);return VARIABLE; }
[ \t\n]+ ; /* ignore whitespace */
. yyerror("Unknown character");
%%
int yywrap(void) {
return 1;
}
calc3.y
%{
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "calc3.h"
/* prototypes */
nodeType *opr(int oper, int nops, ...);
nodeType *id(char * s);
nodeType *con(double value);
void freeNode(nodeType *p);
double ex(nodeType *p);
int yylex(void);
#include <iostream>
#include<map>
#include<set>
using namespace std;
void yyerror(const char *s);
map<string,double> sym; /* symbol table */
map<string,set<double>> m;
%}
%union {
int iValue; /* integer value */
char *sIndex; /* symbol table index */
double flo; /* symbol table index */
nodeType *nPtr; /* node pointer */
};
%token <iValue> INTEGER
%token <flo> FLO
%token <sIndex> VARIABLE
%token WHILE IF PRINT
%nonassoc IFX
%nonassoc ELSE
%left GE LE EQ NE '>' '<' GG LL
%left '+' '-' OR '^' AD IN
%left '*' '/' AND
%nonassoc UMINUS
%nonassoc ADD
%nonassoc NOT
%type <nPtr> stmt expr stmt_list
%%
program:
function { exit(0); }
;
function:
function stmt { ex($2); freeNode($2); }
| /* NULL */
;
stmt:
';' { $$ = opr(';', 2, NULL, NULL); }
| expr ';' { $$ = $1; }
| PRINT expr ';' { $$ = opr(PRINT, 1, $2); }
| VARIABLE '=' expr ';' { $$ = opr('=', 2, id($1), $3); }
| VARIABLE GG ';' { $$ = opr('=', 2, id($1), opr('+', 2, id($1), con(1)));}
| VARIABLE LL ';' { $$ = opr('=', 2, id($1), opr('-', 2, id($1), con(1))); }
| INTEGER AD VARIABLE ';' { $$ = NULL;m[$3].insert($1);}
| INTEGER IN VARIABLE ';' { $$ = NULL; if(m[$3].count($1))cout<<"true"<<endl;else cout<<"false";}
| WHILE '(' expr ')' stmt { $$ = opr(WHILE, 2, $3, $5); }
| IF '(' expr ')' stmt %prec IFX { $$ = opr(IF, 2, $3, $5); }
| IF '(' expr ')' stmt ELSE stmt { $$ = opr(IF, 3, $3, $5, $7); }
| '{' stmt_list '}' { $$ = $2; }
;
stmt_list:
stmt { $$ = $1; }
| stmt_list stmt { $$ = opr(';', 2, $1, $2); }
;
expr:
INTEGER { $$ = con($1); }
| VARIABLE { $$ = id($1); }
| FLO { $$ = con($1); }
| '!' expr %prec NOT { $$ = opr(NOT, 1, $2); }
| '-' expr %prec UMINUS { $$ = opr(UMINUS, 1, $2); }
| '+' expr %prec ADD { $$ = opr(ADD, 1, $2); }
| expr '^' expr { $$ = opr('^', 2, $1, $3); }
| expr '+' expr { $$ = opr('+', 2, $1, $3); }
| expr '-' expr { $$ = opr('-', 2, $1, $3); }
| expr '*' expr { $$ = opr('*', 2, $1, $3); }
| expr '/' expr { $$ = opr('/', 2, $1, $3); }
| expr '<' expr { $$ = opr('<', 2, $1, $3); }
| expr '>' expr { $$ = opr('>', 2, $1, $3); }
| expr GE expr { $$ = opr(GE, 2, $1, $3); }
| expr LE expr { $$ = opr(LE, 2, $1, $3); }
| expr NE expr { $$ = opr(NE, 2, $1, $3); }
| expr EQ expr { $$ = opr(EQ, 2, $1, $3); }
| expr AND expr { $$ = opr(AND, 2, $1, $3); }
| expr OR expr { $$ = opr(OR, 2, $1, $3); }
| '(' expr ')' { $$ = $2; }
;
%%
nodeType *con(double value) {
nodeType *p;
/* allocate node */
if ((p = (nodeType *)malloc(sizeof(nodeType))) == NULL)
yyerror("out of memory");
/* copy information */
p->type = typeCon;
p->con.value = value;
return p;
}
nodeType *id(char * s) {
nodeType *p;
/* allocate node */
if ((p =(nodeType *) malloc(sizeof(nodeType))) == NULL)
yyerror("out of memory");
/* copy information */
p->type = typeId;
p->id.s = s;
return p;
}
nodeType *opr(int oper, int nops, ...) {
va_list ap;
nodeType *p;
int i;
/* allocate node, extending op array */
if ((p = (nodeType *)malloc(sizeof(nodeType) + (nops-1) * sizeof(nodeType *))) == NULL)
yyerror("out of memory");
/* copy information */
p->type = typeOpr;
p->opr.oper = oper;
p->opr.nops = nops;
va_start(ap, nops);
for (i = 0; i < nops; i++)
p->opr.op[i] = va_arg(ap, nodeType*);
va_end(ap);
return p;
}
void freeNode(nodeType *p) {
int i;
if (!p) return;
if (p->type == typeOpr) {
for (i = 0; i < p->opr.nops; i++)
freeNode(p->opr.op[i]);
}
free (p);
}
void yyerror(const char *s) {
fprintf(stdout, "%s\n", s);
}
int main(void) {
yyparse();
return 0;
}
calc3.h----辅助头文件
#include <iostream>
#include<map>
using namespace std;
typedef enum { typeCon, typeId, typeOpr } nodeEnum;
/* constants */
typedef struct {
double value; /* value of constant */
} conNodeType;
/* identifiers */
typedef struct {
char *s; /* subscript to sym array */
} idNodeType;
/* operators */
typedef struct {
int oper; /* operator */
int nops; /* number of operands */
struct nodeTypeTag *op[1]; /* operands, extended at runtime */
} oprNodeType;
typedef struct nodeTypeTag {
nodeEnum type; /* type of node */
union {
conNodeType con; /* constants */
idNodeType id; /* identifiers */
oprNodeType opr; /* operators */
};
} nodeType;
extern map<string,double> sym;
calc3.c---核心处理函数
#include <stdio.h>
#include "calc3.h"
#include "calc3.tab.h"
#include <iostream>
#include<map>
using namespace std;
double ex(nodeType *p) {
if (!p) return 0;
switch(p->type) {
case typeCon: return p->con.value;
case typeId: if(sym.count(p->id.s))return sym[p->id.s];else {cout<<p->id.s<<" is Uninitialized"<<endl;exit(0);}
case typeOpr:
switch(p->opr.oper) {
case WHILE: while(ex(p->opr.op[0])) ex(p->opr.op[1]); return 0;
case IF: if (ex(p->opr.op[0]))
ex(p->opr.op[1]);
else if (p->opr.nops > 2)
ex(p->opr.op[2]);
return 0;
case PRINT: cout<<ex(p->opr.op[0])<<endl;; return 0;
case ';': ex(p->opr.op[0]); return ex(p->opr.op[1]);
case '=': return sym[p->opr.op[0]->id.s] = ex(p->opr.op[1]);
case UMINUS: return -ex(p->opr.op[0]);
case ADD: return ex(p->opr.op[0]);
case '+': return ex(p->opr.op[0]) + ex(p->opr.op[1]);
case '-': return ex(p->opr.op[0]) - ex(p->opr.op[1]);
case '*': return ex(p->opr.op[0]) * ex(p->opr.op[1]);
case '/': if(ex(p->opr.op[1])==0){cout<<"can't divide by 0"<<endl;exit(0);}return ex(p->opr.op[0]) / ex(p->opr.op[1]);
case '<': return ex(p->opr.op[0]) < ex(p->opr.op[1]);
case '>': return ex(p->opr.op[0]) > ex(p->opr.op[1]);
case '^': return (!ex(p->opr.op[0])&&ex(p->opr.op[1]))||(ex(p->opr.op[0])&&!ex(p->opr.op[1]));
case GE: return ex(p->opr.op[0]) >= ex(p->opr.op[1]);
case LE: return ex(p->opr.op[0]) <= ex(p->opr.op[1]);
case NE: return ex(p->opr.op[0]) != ex(p->opr.op[1]);
case EQ: return ex(p->opr.op[0]) == ex(p->opr.op[1]);
case OR: return ex(p->opr.op[0]) || ex(p->opr.op[1]);
case AND: return ex(p->opr.op[0]) && ex(p->opr.op[1]);
case NOT: return !ex(p->opr.op[0]);
}
}
return 0;
}