自己没有研究过。准备工作当然是搜集资料了,先看了一篇教程《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;
}
*/
#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)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;
lexsymb.h文件包含了几个函数的声明以及用来保存行数的全局变量:
extern int lineno;
extern int yyerror(char *msg);
extern int yylex();
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;
}
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文件中规则的编写。