编译原理语法分析实现

本文详细介绍了编译原理中的词法分析过程,包括正则表达式、有限状态自动机和lex/flex工具的使用。同时,讲解了如何构造抽象语法树,并处理语法和类型错误。文中还给出了C语言实现的词法分析器(flex)和语法分析器(bison)的示例代码,以及解析表达式和处理运算符关联性的方法。
摘要由CSDN通过智能技术生成

符号表

EXP ([Ee][-+]?[0-9]+)
TYPE int|float
STRUCT struct
RETURN return
IF if
ELSE else
WHILE while
sixteen [0-9]|[A-F]
sixteennoo [1-9]|[A-F]
eight [0-7]
eightnoo [1-7]
ten [0-9]
tennoo [1-9]
EIGHTINT (0{eightnoo}{eight}*)
SIXTEENINT 0[xX]({sixteennoo}{sixteen}*)
INT ({tennoo}{ten}*)|0
SIXTEENFLOAT 0[xX](({sixteennoo}{sixteen}*)|0)"."{sixteen}+
EIGHTFLOAT 0({eightnoo}{eight}*|0)"."{eight}+
FLOAT ({ten}*"."{ten}+)|({ten}+"."{ten}*)
EFLOAT (({ten}*"."{ten}+)|({ten}+"."{ten}*)|{ten}*){EXP}
WRONG_SIXTEENINT 0[xX]{sixteen}*[G-Zg-z]+[A-Za-z0-9]*
WRONG_EIGHTINT 0{eight}*[89A-Za-z]+[A-Za-z0-9]*
WRONG_EFLOAT ({ten}*"."{ten}*|{ten}+)[eE]({FLOAT}*|[A-Za-z]*))|[{"."}]?[eE][-+]?({ten}+|{ten}*"."{ten}*
ID [A-Za-z_][A-Za-z_0-9]*
SEMI ;
COMMA ,
ASSIGNOP =
PLUS \+
MINUS \-
AND &&
OR \|\|
DOT \.
NOT \!
LP \(
RP \)
LB \[
RB \]
LC \{
RC \}
RELOP >|<|==|<=|>=|!=
COMMENT (\/\/.*)|([/][*]([^(\*/)])*[*][/])
STAR \*
DIV \/
SPACE1 [\t]
SPACE2 [ ]
EOL \n|\r\n
ERROR_TYPE .

Tree.c

struct NODE // 左孩子右兄弟表示,多叉树转化成为二叉树
{
	char name[40];
	struct NODE *firstchild;  // 左孩子
	struct NODE *nextsibling; // 右兄弟
	int line;				  // 列号
	int column;				  // 行号
	char type[40];
	char var_fun_str_name[30]; //文法中实际的输入
};

struct NODE *Create_newnode(char *name, int line, int column, char *type) // 每遇到一个词法单元,就会分配一个树上结点

struct NODE *insert(int num, ...) // 一个父亲结点(非终结符)连接着多个孩子节点(终结符与非终结符)
// 依次读入参数变量,将第一个变量作为父亲节点的孩子结点插入。将第二个变量作为孩子结点的兄弟结点插入到孩子结点的下面。
// 我们要将第一个参数作为变量的数量,真正的变量从第二个开始。这里也用到了可变参数机制。

return node_parent; // 返回归约之后的父亲结点(非终结符)

void print(struct NODE *root, int ceng)
// 按照 dfs 的顺序来遍历,也就是先序遍历
// 碰到节点就打印,然后递归左边,递归右边。对于终结符来说,我们需要打印它的类型以及实际内容,对于非终结符,我们需要打印它的类型以及行号。
    
char *sixteenint_to_int(char *str) // 十六进制转成十进制
    
char *eightint_to_int(char *str) // 八进制转成十进制

Lexical.l

flex进行scanner,将数值存入yylval。

而bison读取yylval之中的值。

{INT} {
	strcpy(str,"INT");  // str用来存储类型名称
	strcpy(str2,yytext);  // str2用来存储该类型具体的东西  (存储到yytext里面)
	nodee = Create_newnode(str,yylineno,column,yytext); // 每遇到一个词法单元,为其分配树上节点
	strcpy(nodee->var_fun_str_name,str2);  // 把结点具体名称维护到name中
	yylval.token_node = nodee; // token_node 是在 syntax.y 中的联合体中定义,
	column+=yyleng;  // 维护列号
	return INT;  // 返回类型名称
}

Syntax.y

定义部分

%{
    //对库函数的引用与全局变量声明与引用
}%
%token<token_node> 终结符

以表达式“ x   o p   y   o p   z x \ op \ y \ op \ z x op y op z”为例,运算符op的关联性决定了运算符嵌套,是通过先将x与y分组还是先将y与z分组来解析。

% l e f t %left left 指定左相关性(将x与y优先分组)
% r i g h t %right right 指定右相关性(将y与z优先分组)
% n o n a s s o c %nonassoc nonassoc 未指定结合性,这意味着“ x   o p   y   o p   z x \ op \ y \ op \ z x op y op z”被视为语法错误。

排在后面的优先级更高

由于else总是匹配最近的if,所以我们定义了一个比else优先级更低的东西

规则部分

产生式左边的非终结符为初始符号,箭头等同于:。

产生式左边的名字为$$

产生式右边的依次为$(1,2,3,…)

产生式之间以;分隔

如果语法没有错误,$$ = insert(num, $1, $2, …)。然后再将父亲节点的内容拷贝进去。

而对于语法错误,我们就不需要将其插入进语法树中了。

规约与移进

Bison总会选择移入

调试

bison -d -v syntax.y

诊断模式

bison -d -t syntax.y
具体规则
Program -> ExtDefList | ExtDefList error(Unexpected character)
ExtDefList ->| ExtDef ExtDefList//定义列表,递归定义
ExtDef -> Specifier ExtDecList SEMI(;) (全局变量)
		| Specifier ExtDecList error (全局变量缺分号)
        | Specifier SEMI(;) 
        | Specifier FunDec CompSt (定义函数实现过程,包括主函数)
        | Specifier FunDec SEMI(;)  //函数声明
		| Specifier error (结构体缺分号)
ExtDecList -> VarDec (声明变量)
            | VarDec COMMA(,) ExtDecList(多个声明变量)
Specifier -> TYPE(int | float) (普通类型)
			| StructSpecifier (结构体类型)
StructSpecifier -> STRUCT(struct) OptTag LC({) DefList RC(}) (定义结构体的产生式)//struct (id){int a;\nfloatb;}(id2)
				| STRUCT(struct) Tag (使用已经定义的结构体)
    			| STRUCT(struct) OptTag LC({) DefList error (struct})
OptTag ->| ID([A-Za-z_][A-Za-z_0-9]*) //自定义数据类型
Tag -> ID([A-Za-z_][A-Za-z_0-9]*) 
VarDec -> ID([A-Za-z_][A-Za-z_0-9]*) | VarDec LB([) INT RB(]) //array[10]
        | VarDec LB([) ID([A-Za-z_][A-Za-z_0-9]*) RB(])     //array[i] 
		| VarDec LB([) error RB(]) ([]中括号中间不是int或id)
        | VarDec LB INT error  (],例如a[3)      
        | VarDec LB ID error 	(],例如a[i)
FunDec -> ID([A-Za-z_][A-Za-z_0-9]*) LP(() VarList RP()) //function(int a,int b)
        | ID([A-Za-z_][A-Za-z_0-9]*) LP(() RP())       //无参函数
        | ID LP error  (函数无参数列表缺))                              
        | ID LP error RP  (函数参数列表中参数错误)                              
        | ID LP VarList error (函数参数列表缺))               
VarList -> ParamDec COMMA(,) VarList | ParamDec //int a, float b.....用逗号隔开的一堆变量声明
ParamDec -> Specifier VarDec                               
CompSt -> LC({) DefList StmtList RC(})  //语句块,作用域         
StmtList ->| Stmt StmtList       //语句链,语句1+语句2.....                      
Stmt -> Exp SEMI(;)      //Stmt:各种语句操作     
      | CompSt                                  
      | RETURN(return) Exp SEMI(;)                                 
      | IF LP(() Exp RP()) Stmt %prec LOWER_THAN_ELSE   //ifelse语句,%prec是设置优先级,让else能跟最近的if匹配
      | IF LP(() Exp RP()) Stmt ELSE(else) Stmt                   
      | WHILE LP(() Exp RP()) Stmt               
      | ELSE Stmt   (没有if只有else)                               
      | Exp error 	(表达式缺分号;)
	  | RETURN Exp error (return缺分号;)                           
      | IF LP error RP Stmt %prec LOWER_THAN_ELSE (if跟的()中的表达式错误)          
      | IF LP error RP Stmt ELSE Stmt  (if跟的()中的表达式错误)       
      | IF LP Exp error Stmt ELSE Stmt 	(if))                   
      | WHILE LP error RP Stmt (while()中语法错误)     
      | WHILE LP Exp error Stmt (while语句缺))               
DefList ->| Def DefList        //定义语句链          
Def -> Specifier DecList SEMI    //这个是变量定义不是声明吧?                                 
      | Specifier error SEMI (变量声明错误)  
      | Specifier DecList error (变量声明缺分号;)             
DecList -> Dec | Dec COMMA DecList    //定义并操作:int a = 10,b = 1 + 2......                
Dec -> VarDec  
    | VarDec ASSIGNOP Exp 
    | VarDec ASSIGNOP error  ( =右边不是表达式 )
Exp -> Exp ASSIGNOP Exp    (EXP为表达式)  (表达式 = 表达式)
    | Exp AND Exp (表达式 && 表达式)
    | Exp OR Exp  (表达式 || 表达式)
    | Exp RELOP Exp  (表达式 bool运算 表达式)
    | Exp PLUS Exp   (表达式 + 表达式)
    | Exp MINUS Exp  (表达式 - 表达式)
    | Exp STAR Exp (表达式 * 表达式)
    | Exp DIV Exp (表达式 / 表达式)
    | LP Exp RP ((表达式))
    | MINUS Exp (-表达式)
    | NOT Exp (!表达式)
    | ID LP Args RP  ( ID (  变量  )  )
    | ID LP RP ( ID ( )| Exp LB Exp RB ( 表达式 [ 表达式 ] )
    | Exp DOT ID (表达式 . ID)
    | ID
    | INT
    | FLOAT
    | EIGHTINT
    | SIXTEENINT
    | EIGHTFLOAT
    | SIXTEENFLOAT
    | EFLOAT
    | Exp LB Exp COMMA Exp RB (表达式 ( 表达式 , 表达式 ) )
    | Exp LB error RB  (表达式[]里语法错误)
    | Exp LB error (缺]| ID LP error (缺)| Exp ASSIGNOP error
    | Exp AND error
    | Exp OR error
    | Exp RELOP error
    | Exp PLUS error
    | Exp MINUS error
    | Exp STAR error
    | Exp DIV error
    | LP error RP
    | MINUS error
    | NOT error
    | ID LP error RP  ( ()括号中的内容错误 )
    | LP Exp error   (缺少))
    | LB Exp error  (缺少])
Args -> Exp COMMA Args | Exp    (Args: 变量)  (Args -> 表达式 , 变量 | 表达式)

用户函数(main函数部分)

int main(int argc,char** argv)
{
	FILE* f=fopen(argv[1],"r");
	yyrestart(f);
	yyparse();
	if(!ERROR_A&&!ERROR_B) // 没有报错生成语法树
	{
		print(node,0); // 从第0层开始dfs先序遍历输出
	}
	fclose(f);
	return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这个里面的都是测试数据,总共得分5分。从控制台输入,不能从文件中读取。实现了基本功能,加分项目都没有去实现,没有函数数组这些的实现。这是用C++语言的,新建parser类别要选C++,其他对于VS的配置和C语言一样。for语句用的是枚举所有情况,你可以自行修改。 对预备工作中自然语言描述的简化C编译器的语言特性的语法,设计上下文无关文法进行描述 借助Yacc工具实现语法分析器 考虑语法树的构造: 1.语法树数据结构的设计:节点类型的设定,不同类型节点应保存哪些信息,多叉树的实现方式 2.实现辅助函数,完成节点创建、树创建等功能 3.利用辅助函数,修改上下文无关文法,设计翻译模式 4.修改Yacc程序,实现能构造语法树的分析器 考虑符号表处理的扩充 1.完成语法分析后,符号表项应增加哪些标识符的属性,保存语法分析的结果 2.如何扩充符号表数据结构,Yacc程序如何与Lex程序交互,正确填符号表项 以一个简单的C源程序验证你的语法分析器,可以文本方式输出语法树结构,以节点编号输出父子关系,来验证分析器的正确性,如下例: main() { int a, b; if (a == 0) a = b + 1; } 可能的输出为: 0 : Type Specifier, integer, Children: 1 : ID Declaration, symbol: a Children: 2 : ID Declaration, symbol: b Children: 3 : Var Declaration, Children: 0 1 2 4 : ID Declaration, symbol: a Children: 5 : Const Declaration, value:0, Children: 6 : Expr, op: ==, Children: 4 5 7 : ID Declaration, symbol: a Children: 8 : ID Declaration, symbol: b Children: 9 : Const Declaration, value:1, Children: 10: Expr, op: +, Children: 8 9 11: Expr, op: =, Children: 7 10 12: if statement, Children: 6 11 13: compound statement, Children: 3 12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值