自己动手写一个编译器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的官方文档里找到模板渲染的代码. ...
随机推荐
- hdu1503(最长公共子序列)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1503 题意:由两个字符串构造出另一个字符串,该字符串包含前两个字符串(按字符顺序,但不一定连续),使该 ...
- HDU 4869 Turn the pokers(推理)
HDU 4869 Turn the pokers 题目链接 题意:给定n个翻转扑克方式,每次方式相应能够选择当中xi张进行翻转.一共同拥有m张牌.问最后翻转之后的情况数 思路:对于每一些翻转,假设能确 ...
- Ubuntu——grub rescue 主引导修复
长期使用windows 和 ubuntu 人双系统,很可能遇到沉重的一个系统,或以其他方式加盟分区,导致系统重新启动时 : GRUB loading error:unknow filesystem g ...
- Android应用开发-小巫CSDN博客clientJsoup篇
Android应用开发-小巫CSDN博客clientJsoup篇 距上一篇博客已经过去了两个星期,小巫也认为很抱歉,由于在忙着做另外一个项目,差点儿抽不出空来,这不小巫会把剩下的博文全部在国庆补上.本 ...
- 使用ReactiveCocoa实现iOS平台响应式编程
使用ReactiveCocoa实现iOS平台响应式编程 ReactiveCocoa和响应式编程 在说ReactiveCocoa之前,先要介绍一下FRP(Functional Reactive Prog ...
- hdu 4472 Count (递推)
Count Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Subm ...
- Learning To Rank之LambdaMART前世今生
1. 前言 我们知道排序在非常多应用场景中属于一个非常核心的模块.最直接的应用就是搜索引擎.当用户提交一个query.搜索引擎会召回非常多文档,然后依据文档与query以及用户的相关程度对 ...
- TCP header
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3Vzc2VyNDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA ...
- hdu2089不要62(数位dp)
#include <stdio.h> #include <string.h> ][]; ]; /* dp[i][0] 不含62,4 dp[i][1] 2开头 dp[i][2] ...
- 《深入Java虚拟机》笔记:指令集 (转)
<深入Java虚拟机>笔记:指令集 指令 含义 iconst_m1 把int型常量-1压入栈中 iconst_0 把int型常量压入栈中 fconst_1 把float型常量1压入栈中 ...