flex&bison 学习笔记(1)

最近用的项目是用脚本写GUI界面,空闲时间也想自己写个脚本系统玩一玩,以前用的都是别人写好的脚本引擎,比如:JavaScript、VBScript、lua……
自己没有研究过。准备工作当然是搜集资料了,先看了一篇教程《Implementing a Scripting Engine》,说得不错,打算也采用lex和yacc来做词法和语法的分析(全都自己写太累了,有现成的何必不用呢?)。
但是郁闷的是,该教程附带的例子只有一个lex词法分析的,yacc的没有,不管那么多,先跑起来看看,恩,不知所云,不过程序跑完整了。能把输入的文本按规则返回token。不过怎么编写lex文件还摸不到门道。
上网搜了半天关于yacc的资料,结果有语法文件的都编译不通过,郁闷!~~~~~~
今天决定分开来看,首先是flex文件,
%{
#include 
<stdio.h>
#include 
<string.h>
#include 
<io.h>
#include 
"lexsymb.h"
#include 
"parse.cpp.h"
%}

LETTER      [a
-zA-Z_]
DIGIT       [
0-9]
IDENT       
{LETTER}({LETTER}|{DIGIT})*
STR         \
"[^\"]*\"
CHAR        '{LETTER}'
WSPACE      [ \t]
+
INTEGER     [
-]*{DIGIT}+
bool        "true"|"false"

%%
{WSPACE} ;/* Eat all space */
\n 
{lineno++;}

"="         {return ASSIGN;}
"=="        {return EQUAL;}
"!="        {return NOTEQUAL;}
"if"        {return IF;}
"else"      {return ELSE;}
"print"     {return PRINT;}
"input"     {return INPUT;}
"+"         {return CONCAT;}
";"         {return END_STMT;}
"("         {return OPEN_PAR;}
")"         {return CLOSE_PAR;}
"{"         {return BEGIN_CS;}
"}"         {return END_CS;}
{bool}      { printf("bool value\n"); };
{INTEGER} {
yylval.str 
= new char[strlen(yytext) + 1];
strcpy(yylval.str, yytext);
printf(
"Integer:%s\n", yylval.str);
return INTEGER;
}

{CHAR} {int iSize = strlen(yytext) - 1;
yylval.str 
= new char[iSize];
strncpy(yylval.str, 
&yytext[1], iSize - 1);
yylval.str[iSize 
- 1= 0;
printf(
"Char:%s\n", yylval.str);
return STRING;
}

{STR} int iSize = strlen(yytext) - 1;
yylval.str 
= new char[iSize];
strncpy(yylval.str, 
&yytext[1], iSize - 1);
yylval.str[iSize 
- 1= 0;
printf(
"String:%s\n", yylval.str);
return STRING;
}

{IDENT} {
yylval.str 
= new char[strlen(yytext) + 1];
strcpy(yylval.str, yytext);
printf(
"Identifier:%s\n", yylval.str);
return ID;
}

{printf("Undefined symbo at line %d\n", lineno);return ERROR_TOKEN;}
%%
int yywrap()
{
return 1;
}

/* YYSTYPE yylval; */
int lineno = 0;
/*int main()
{
while ( yylex() )
{
}
return 0;
}
*/

这个文件编译出来的东西没有问题,可以执行,当然要单独执行就要把最后的那些注释掉的代码恢复。lex文件分成三部分,用%%来隔开,第一部分是声明部分可以放C和lex的全局声明,第二部分是模式以及动作代码,第三部分是附加c代码(可以不写)
parse.cpp.h是和yacc同时运行时有yacc生成的头文件,包含了token符号的定义以及YYSTYPE的定义:
typedef union {
    
char* str;
}
 YYSTYPE;
#define    ERROR_TOKEN    258
#define    IF    259
#define    ELSE    260
#define    PRINT    261
#define    INPUT    262
#define    ASSIGN    263
#define    EQUAL    264
#define    NOTEQUAL    265
#define    CONCAT    266
#define    END_STMT    267
#define    OPEN_PAR    268
#define    CLOSE_PAR    269
#define    BEGIN_CS    270
#define    END_CS    271
#define    ID    272
#define    INTEGER    273
#define    STRING    274


extern YYSTYPE yylval;
在.NET 2005下编译必须要包含io.h,否则会报告没有定义的函数  int __cdecl isatty(int)
lexsymb.h文件包含了几个函数的声明以及用来保存行数的全局变量:
extern int lineno;
extern int yyerror(char *msg);
extern int yylex();

接下来是yacc文件了:这是从网上找到的很简单的一个例子,修改了一下以和我的lex文件相符合:
%union {
    
char* str;
}
%{

#include 
<stdio.h>
#include 
<string.h>
#include 
<io.h>
#include 
<malloc.h>
#include 
"lexsymb.h"
%}
%token ERROR_TOKEN
%token IF
%token ELSE
%token PRINT
%token INPUT
%token ASSIGN
%token EQUAL
%token NOTEQUAL
%token CONCAT
%token END_STMT
%token OPEN_PAR
%token CLOSE_PAR
%token BEGIN_CS
%token END_CS
%token ID
%token INTEGER
%token STRING
%%
file : record file
| record
;
record : ID EQUAL INTEGER {
printf(
"%s is %s years old!!!\n", $1, $3); }
;
%%
int main()
{
yyparse();
return 0;
}
int yyerror(char *msg)
{
printf(
"Error encountered: %s \n", msg);
return 0;
}

yacc文件也跟lex文件一样分成三部分,用%%来分隔,
第一部分中的%union代码 声明了 Yacc 值栈,并且外部变量 yylval 和 yyval 有等于这个联合的类型。如果使用 -d 选项调用 Yacc,则把联合声明复制到 头文件中(我这里是parse.cpp.h)。二者选一的,联合也可以在头文件中声明,使用 typedef 来定义变量 YYSTYPE 去表示这个联合
token的定义也在第一部分,-d参数会把这些token的id复制到头文件中。
先了解了这么多,接下来的任务是yacc文件中规则的编写。
原文地址:https://www.cnblogs.com/hyamw/p/690300.html