自己动手写一个编译器Tiny语言解析器实现
然后,上一篇文章简介Tiny词法分析,实现语言。本文将介绍Tiny的语法分析器的实现。
1 Tiny语言的语法
下图是Tiny在BNF中的文法。
文法的定义能够看出。INNY语言有以下特点:
1 程序共同拥有5中语句:if语句,repea语句,read语句,write语法和assign语句。
2 if语句以end作为结束符号,if语句和repeat语句同意语句序列作为主体。
3 输入/输出由保留字read和write開始。read语句一次仅仅读出一个变量,而write语句一次仅仅写出一个表达式。
2 Tiny编译器的语法树结构
TINY有两种主要的结构类型:语句和表达式。语句共同拥有5类:(if语句、repeat语句、assign语句、read语句和read语句)。表达式共同拥有3类(算符标的是、常量表达式和标识符表达式)。因此,语法树节点首先安装它是语句还是表达式来进行分类,接着依据语句或表达式的种类进行再次分类。
树节点最大可有3个孩子的结构(仅在带有else部分的if
语句才用到)。
语句通过同属域而不是子域来排序,即由父亲到他的孩子的唯一物理连接是到最左孩子的。孩子则在一个标准连接表中自左向右连接到一起,这样的连接称作同属连接,用于差别父子连接。
左边的图片是同属连接,右边的图片表示父子连接。
一个Tiny语法树节点的C声明例如以下:
/*********** Syntax tree for parsing ************/
/**************************************************/
typedef enum {StmtK,ExpK} NodeKind;
typedef enum {IfK,RepeatK,AssignK,ReadK,WriteK} StmtKind;
typedef enum {OpK,ConstK,IdK} ExpKind;
/* ExpType is used for type checking */
typedef enum {Void,Integer,Boolean} ExpType;
#define MAXCHILDREN 3
typedef struct treeNode
{ struct treeNode * child[MAXCHILDREN];
struct treeNode * sibling;
int lineno;
NodeKind nodekind;
union { StmtKind stmt; ExpKind exp;} kind;
union { TokenType op;
int val;
char * name; } attr;
ExpType type; /* for type checking of exps */
} TreeNode;
/**************************************************/
以下画出语法树的结构。用矩形框表示语句节点。用圆形框或椭圆形框表示表达式节点。
仍然以Tiny语言的阶乘为例,给出Tiny程序的语法树。
{ Sample program
in TINY language -
computes factorial
}
read x; { input an integer }
if 0 < x then { don't compute if x <= 0 }
fact := 1;
repeat
fact := fact * x;
x := x - 1
until x = 0;
write fact { output factorial of x }
end
3 使用Yacc生成Tiny分析程序
源代码例如以下。相应这第一节给出的Tiny的BNF文法。
%{
#define YYPARSER /* distinguishes Yacc output from other code files */
#include "globals.h"
#include "util.h"
#include "scan.h"
#include "parse.h"
#define YYSTYPE TreeNode *
static char * savedName; /* for use in assignments */
static int savedLineNo; /* ditto */
static TreeNode * savedTree; /* stores syntax tree for later return */
%}
%token IF THEN ELSE END REPEAT UNTIL READ WRITE
%token ID NUM
%token ASSIGN EQ LT PLUS MINUS TIMES OVER LPAREN RPAREN SEMI
%token ERROR
%% /* Grammar for TINY */
program : stmt_seq
{ savedTree = $1;}
;
stmt_seq : stmt_seq SEMI stmt
{ YYSTYPE t = $1;
if (t != NULL)
{ while (t->sibling != NULL)
t = t->sibling;
t->sibling = $3;
$$ = $1; }
else $$ = $3;
}
| stmt { $$ = $1; }
;
stmt : if_stmt { $$ = $1; }
| repeat_stmt { $$ = $1; }
| assign_stmt { $$ = $1; }
| read_stmt { $$ = $1; }
| write_stmt { $$ = $1; }
| error { $$ = NULL; }
;
if_stmt : IF exp THEN stmt_seq END
{ $$ = newStmtNode(IfK);
$$->child[0] = $2;
$$->child[1] = $4;
}
| IF exp THEN stmt_seq ELSE stmt_seq END
{ $$ = newStmtNode(IfK);
$$->child[0] = $2;
$$->child[1] = $4;
$$->child[2] = $6;
}
;
repeat_stmt : REPEAT stmt_seq UNTIL exp
{ $$ = newStmtNode(RepeatK);
$$->child[0] = $2;
$$->child[1] = $4;
}
;
assign_stmt : ID { savedName = copyString(tokenString);
savedLineNo = lineno; }
ASSIGN exp
{ $$ = newStmtNode(AssignK);
$$->child[0] = $4;
$$->attr.name = savedName;
$$->lineno = savedLineNo;
}
;
read_stmt : READ ID
{ $$ = newStmtNode(ReadK);
$$->attr.name =
copyString(tokenString);
}
;
write_stmt : WRITE exp
{ $$ = newStmtNode(WriteK);
$$->child[0] = $2;
}
;
exp : simple_exp LT simple_exp
{ $$ = newExpNode(OpK);
$$->child[0] = $1;
$$->child[1] = $3;
$$->attr.op = LT;
}
| simple_exp EQ simple_exp
{ $$ = newExpNode(OpK);
$$->child[0] = $1;
$$->child[1] = $3;
$$->attr.op = EQ;
}
| simple_exp { $$ = $1; }
;
simple_exp : simple_exp PLUS term
{ $$ = newExpNode(OpK);
$$->child[0] = $1;
$$->child[1] = $3;
$$->attr.op = PLUS;
}
| simple_exp MINUS term
{ $$ = newExpNode(OpK);
$$->child[0] = $1;
$$->child[1] = $3;
$$->attr.op = MINUS;
}
| term { $$ = $1; }
;
term : term TIMES factor
{ $$ = newExpNode(OpK);
$$->child[0] = $1;
$$->child[1] = $3;
$$->attr.op = TIMES;
}
| term OVER factor
{ $$ = newExpNode(OpK);
$$->child[0] = $1;
$$->child[1] = $3;
$$->attr.op = OVER;
}
| factor { $$ = $1; }
;
factor : LPAREN exp RPAREN
{ $$ = $2; }
| NUM
{ $$ = newExpNode(ConstK);
$$->attr.val = atoi(tokenString);
}
| ID { $$ = newExpNode(IdK);
$$->attr.name =
copyString(tokenString);
}
| error { $$ = NULL; }
;
%%
int yyerror(char * message)
{ fprintf(listing,"Syntax error at line %d: %s\n",lineno,message);
fprintf(listing,"Current token: ");
printToken(yychar,tokenString);
Error = TRUE;
return 0;
}
TreeNode * parse(void)
{ yyparse();
return savedTree;
}
4 执行程序
先点击下载完整可执行代码,按以下的步骤便可执行。
Step 1 在命令行输入
$ ./build.sh
Step 2 改动生成的y.tab.c代码。
yacc生成的y.tab.c中使用yylex()函数来获取字符,须要替换成我们在上一篇文章中提供的由lex生成的getToken()函数。
在y.tab.c中找到
yychar = yylex ();
替换成
yychar = getToken ();
Step 3 make && run
在命令行中输入例如以下命令便可执行程序
$ make
$ ./tiny.out sample.tny
程序执行后会打印出语法树。节点间关系以空格标识。
TINY COMPILATION: sample.tny
Syntax tree:
Read: x
If
Op: <
Const: 0
Id: x
Assign to: fact
Const: 1
Repeat
Assign to: fact
Op: *
Id: fact
Id: x
Assign to: x
Op: -
Id: x
Const: 1
Op: =
Id: x
Const: 0
Write
Id: fact
大家能够对比着第二节给出的图片,查看输出的语法树结构,体会一下Tiny语法树中相应的数据结构的设计。
5 小结
本文主要介绍了Tiny语言的语法分析器的实现过程。下一篇文章将介绍Tiny语言的语义分析,主要包含符号表的生成和类型检查的算法。
版权声明:本文博主原创文章,博客,未经同意不得转载。
自己动手写一个编译器Tiny语言解析器实现的更多相关文章
- 自己动手实现一个简单的JSON解析器
1. 背景 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.相对于另一种数据交换格式 XML,JSON 有着诸多优点.比如易读性更好,占用空间更少等.在 ...
- 用c#写一个json的万能解析器
CommonJsonModel .cs /// <summary> /// 万能JSON解析器 /// </summary> public class CommonJsonMo ...
- 死磕 java同步系列之自己动手写一个锁Lock
问题 (1)自己动手写一个锁需要哪些知识? (2)自己动手写一个锁到底有多简单? (3)自己能不能写出来一个完美的锁? 简介 本篇文章的目标一是自己动手写一个锁,这个锁的功能很简单,能进行正常的加锁. ...
- 动手写一个简单版的谷歌TPU-指令集
系列目录 谷歌TPU概述和简化 基本单元-矩阵乘法阵列 基本单元-归一化和池化(待发布) TPU中的指令集 SimpleTPU实例: (计划中) 拓展 TPU的边界(规划中) 重新审视深度神经网络中的 ...
- 动手写一个简单的Web框架(Werkzeug路由问题)
动手写一个简单的Web框架(Werkzeug路由问题) 继承上一篇博客,实现了HelloWorld,但是这并不是一个Web框架,只是自己手写的一个程序,别人是无法通过自己定义路由和返回文本,来使用的, ...
- 动手写一个简单版的谷歌TPU-矩阵乘法和卷积
谷歌TPU是一个设计良好的矩阵计算加速单元,可以很好的加速神经网络的计算.本系列文章将利用公开的TPU V1相关资料,对其进行一定的简化.推测和修改,来实际编写一个简单版本的谷歌TPU.计划实现到行为 ...
- 死磕 java线程系列之自己动手写一个线程池
欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写 ...
- 自己动手写一个服务网关-java
自己动手写一个服务网关 原文链接:https://www.cnblogs.com/bigben0123/p/9252444.html 引言 什么是网关?为什么需要使用网关? 如图所示,在不使用网关的情 ...
- 动手写一个简单的Web框架(模板渲染)
动手写一个简单的Web框架(模板渲染) 在百度上搜索jinja2,显示的大部分内容都是jinja2的渲染语法,这个不是Web框架需要做的事,最终,居然在Werkzeug的官方文档里找到模板渲染的代码. ...
随机推荐
- cocos2d-x 贝塞尔曲线的简单运用(CCBezierTo,CCBezierBy)
原文链接:http://blog.csdn.net/we000636/article/details/8616355 一.贝赛尔曲线简单介绍 贝塞尔曲线是应用于二维图形应用程序的数学曲线.曲线的定义有 ...
- Linux Server
Linux Server CentOS 6.3下配置iSCSI网络存储 摘要: 一.简介iSCSI(internet SCSI)技术由IBM公司研究开发,是一个供硬件设备使用的.可以在IP协议的上层运 ...
- WPF换肤之七:异步
原文:WPF换肤之七:异步 在WinForm时代,相信大家都遇到过这种情形,如果在程序设计过程中遇到了耗时的操作,不使用异步会导致程序假死.当然,在WPF中,这种情况也是存在的,所以我们就需要寻找一种 ...
- CSS3 3D旋转动画代码实例
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 熬之滴水穿石:Spring--精简的J2EE(6)
48--曾用过的View 在Spring MVC架构中View实际上是有多种选择的.JSP是首选的view,实际上在J2E ...
- Swing界面刷新问题(转)
在Java Swing编程中,往往会遇到需要动态刷新界面的时候,例如动态刷新JLabel的文本,JTextField里的文本等等.但是往往却没有达到我们预期的效果,我相信很多朋友都遇到过本文将要说的这 ...
- 飘逸的python - 发送带各种类型附件的邮件
上一篇博文演示了如何发送简单的邮件,这一篇将演示如何发送各种类型的附件. 基本思路就是,使用MIMEMultipart来标示这个邮件是多个部分组成的,然后attach各个部分.如果是附件,则add_h ...
- Big Event in HDU(杭电1171)(多重背包)和(母函数)两种解法
Big Event in HDU Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others ...
- Hibernate操作Clob数据类型
在POJO字符串可以声明为一个大型对象java.lang.String要么java.sql.Clob种类. 当程序从数据库加载Clob数据的类型.负荷只有一个Clob数据的逻辑指针类型.我们需要通过使 ...
- hash表、hash算法
概念: 散列表(Hash table.也叫哈希表),是依据关键码值(Key value)而直接进行訪问的数据结构. 也就是说,它通过把关键码值映射到表中一个位置来訪问记录,以加快查找的速度.这个映射函 ...