Flex&Bison(四)规则引擎

本文没有真的实现规则引擎,还是跟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;
}

 

 

©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页