好久没有更新了,主要是研究了一段时间的C++的面向对象的设计方式和更好的架构程序的思路。其实今天这部分代码早已完成,但希望能够更好的给大家讲解如何做一个可用的新编程语言。
上次说到Bison的语义分析功能,但并没有添加对应的语义处理功能,我们这次就构建一个描述语法的抽象语法树。
首先Bison的语义处理功能是十分方便的,只要在后面添加C++的语义动作代码就可以了,但注意,$$ $1 $2 $3
则是内部变量,$$
表示归结元素,其余表示产生式中的元素,(脚本也是其中的$
)。
而这些元素具体是什么变量,则是根据我们之前定义的元素变量名而定的。
所以我们的处理器变成了这个样子:
/* parser.y */
%{
#include <stdio.h>
#include "State.h"
extern int yylex();
extern int yylineno;
extern char* yytext;
extern int yyleng;
extern State* root;
void yyerror(const char *s);
%}
%union {
State *s;
char *str = NULL;
}
%token <str> ID STRING SCRIPT
%type <s> list item bnf_item bnf_list symbol_list symbol name
%start list
%%
/* 总的混合bnf和脚本的列表 */
list : item { $$ = new State(); $$->AddChildrenState($1); root = $$; }
| list item { $1->AddChildrenState($2); $$ = $1; }
;
/* 可以是bnf或脚本 */
item : bnf_item { $$ = $1; }
| SCRIPT { $$ = new State(); $$->state_type = script; $$->script = $1; }
;
/* 一行bnf的定义 */
bnf_item : symbol ‘=‘ bnf_list ‘;‘ { $1->AddChildrenState($3); $$ = $1; $$->isList = false; }
;
/* bnf后面的部分 */
bnf_list : symbol_list { $$ = $1; }
| bnf_list ‘|‘ symbol_list { $1->AddBrotherState($3); $$ = $1; }
;
/* 一条bnf项的列表 */
symbol_list : symbol { $$ = new State(); $$->state_type = temporality; $$->AddChildrenState($1); }
| symbol_list symbol { $1->AddChildrenState($2); $$ = $1; }
;
/* 可用的bnf符号 */
symbol : ‘<‘ name ‘>‘ { $$ = $2; $$->state_type = statement; }
| ‘[‘ name ‘]‘ { $$ = $2; $$->state_type = terminal; }
| ‘e‘ { $$ = new State(); $$->state_type = epsilon; }
| STRING { $$ = new State(); $$->state_type = constant; $$->state_const = $1; }
| SCRIPT { $$ = new State(); $$->state_type = script; $$->script = $1; }
;
/* 名字,并且可以定义实例名 */
name : ID { $$ = new State();
$$->state_class = $1; }
| ID ‘:‘ ID { $$ = new State();
$$->state_class = $1;
$$->state_var = $3; }
;
%%
void yyerror(const char* s){
fprintf(stderr, "%s \n", s);
fprintf(stderr, "line %d: ", yylineno);
fprintf(stderr, "error %s \n", yytext);
}
%type <s>
就是根据union中的元素名定义,将当前节点绑定上一个对应的成员名。
这些动作代码十分重要,但也思路很清晰,就是将当前的逻辑结构转成抽象语法树,保存起来,供我们下一步的运算使用。
之后我们只需要解析这个ast,就能知道我们的BNF语法的语法结构了。
我们做的目前,并没有一个很令人惊讶的结论,但我们是在自己编写一个可靠的编译器,词法分析器,语法分析器都将是我们手工编写的,而这些代码,正是解析语法分析器配置语法的第一步。
不用太过心急,因为,想实现一个可用的LALR语法分析器,工作量还是较大的,但我还是希望,能将其最重要的部分分享给大家。
那么下面,我们就解析一下生成的ast,将BNF范式提取出来吧。
这是一个BNFParser类:
/*
* @Author: sxf
* @Date: 2015-04-17 10:05:26
* @Last Modified by: sxf
* @Last Modified time: 2015-04-17 11:06:18
*/
#ifndef BNF_PARSER_H
#define BNF_PARSER_H
#include "afx.h"
#include "State.h"
class BNFParser {
public:
State* Analysis(const char* filename);
// for debug
void printTree();
private:
State* state_root;
// for debug
void printNode(State* s,int d);
};
#endif // BNF_PARSER_H
/*
* @Author: sxf
* @Date: 2015-04-17 10:30:02
* @Last Modified by: sxf
* @Last Modified time: 2015-04-17 14:06:04
*/
#include "BNFParser.h"
#include "parser.hpp"
#include <stdio.h>
extern FILE* yyin;
State* root = NULL;
State* BNFParser::Analysis(const char* filename) {
/* open the file and change the yyin stream. */
FILE* file_in;
if ((file_in=fopen(filename,"r"))==NULL) {
printf("error on open %s file!",filename);
getchar();
return NULL;
}
yyin = file_in;
yyparse();
state_root = root;
/* you should close the file. */
fclose(file_in);
return root;
}
void BNFParser::printNode(State* s,int d)
{
if (s == NULL) return;
for (int i = 0; i<d; ++i)
printf(" ");
if (s->state_type == statement || s->state_type == terminal)
printf("%s %s",s->state_class,s->state_var);
if (s->state_type == constant)
printf("%s",s->state_const);
if (s->state_type == temporality)
printf("temp node");
if (!s->isList)
printf(" (not list)");
if (s->Repeatable >0 )
{
if (s->Repeatable == 1) printf(" ?");
if (s->Repeatable == 2) printf(" +");
if (s->Repeatable == 3) printf(" *");
}
printf("\n");
printNode(s->children,d+1);
printNode(s->brother,d);
}
void BNFParser::printTree()
{
printNode(state_root,0);
}
这样,再写一个main函数,就可以测试我们的代码是否正确了,大家可用用下面的代码生成一下测试看看
bison -o Parser.cpp parser.y
时间: 2024-10-10 18:17:40