然后,上一篇文章简介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语言解析器实现的更多相关文章

  1. 自己动手实现一个简单的JSON解析器

    1. 背景 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.相对于另一种数据交换格式 XML,JSON 有着诸多优点.比如易读性更好,占用空间更少等.在 ...

  2. 用c#写一个json的万能解析器

    CommonJsonModel .cs /// <summary> /// 万能JSON解析器 /// </summary> public class CommonJsonMo ...

  3. 死磕 java同步系列之自己动手写一个锁Lock

    问题 (1)自己动手写一个锁需要哪些知识? (2)自己动手写一个锁到底有多简单? (3)自己能不能写出来一个完美的锁? 简介 本篇文章的目标一是自己动手写一个锁,这个锁的功能很简单,能进行正常的加锁. ...

  4. 动手写一个简单版的谷歌TPU-指令集

    系列目录 谷歌TPU概述和简化 基本单元-矩阵乘法阵列 基本单元-归一化和池化(待发布) TPU中的指令集 SimpleTPU实例: (计划中) 拓展 TPU的边界(规划中) 重新审视深度神经网络中的 ...

  5. 动手写一个简单的Web框架(Werkzeug路由问题)

    动手写一个简单的Web框架(Werkzeug路由问题) 继承上一篇博客,实现了HelloWorld,但是这并不是一个Web框架,只是自己手写的一个程序,别人是无法通过自己定义路由和返回文本,来使用的, ...

  6. 动手写一个简单版的谷歌TPU-矩阵乘法和卷积

    谷歌TPU是一个设计良好的矩阵计算加速单元,可以很好的加速神经网络的计算.本系列文章将利用公开的TPU V1相关资料,对其进行一定的简化.推测和修改,来实际编写一个简单版本的谷歌TPU.计划实现到行为 ...

  7. 死磕 java线程系列之自己动手写一个线程池

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写 ...

  8. 自己动手写一个服务网关-java

    自己动手写一个服务网关 原文链接:https://www.cnblogs.com/bigben0123/p/9252444.html 引言 什么是网关?为什么需要使用网关? 如图所示,在不使用网关的情 ...

  9. 动手写一个简单的Web框架(模板渲染)

    动手写一个简单的Web框架(模板渲染) 在百度上搜索jinja2,显示的大部分内容都是jinja2的渲染语法,这个不是Web框架需要做的事,最终,居然在Werkzeug的官方文档里找到模板渲染的代码. ...

随机推荐

  1. 最简单的视音频播放演示样例5:OpenGL播放RGB/YUV

    ===================================================== 最简单的视音频播放演示样例系列文章列表: 最简单的视音频播放演示样例1:总述 最简单的视音频 ...

  2. SQL语句查询数据库的触发器、存储过程、视图以及表的SQL语句

    Sql Server数据库用SQL语句查询方法如下: select name from sysobjects where xtype='TR' --所有触发器 select name from sys ...

  3. 浅谈JAVA ThreadPoolExecutor(转)

    这篇文章分为两部分,前面是ThreadPoolExecutor的一些基本知识,后一部分则是Mina中一个特殊的ThreadPoolExecutor代码解析.算是我的Java学习笔记吧. 基础 在我看来 ...

  4. eclipse failed to create the java virtual machine 问题图文解析(转)

    clipse failed to create  the java virtual  machine 解决方法: 1.问题现象 2.java虚拟机初始化失败!寻找eclipse解压路径 3.寻找ecl ...

  5. Rational Rose的四种视图介绍

    Rose模型中有四种视图:Use Case View(用例视图),Logical View(逻辑视图),Component View(组建视图)和Deployment View(配置视图). 用例视图 ...

  6. 教你怎么去一个APP的JSON数据,你懂的

    今天闲着没事.谁让我今天是光棍节呢,算给大家一个福利.没事逛着应用市场.想找个应用高仿下,突然发现一个应用竟然跟我一个名字尼玛,尼玛应用界面做的非常easy.我认为应该不难. 惯性操作想去破解APK. ...

  7. wamp5中的apache不能启动,80端口被占用

    在wamp中apache中的httpd.conf文件中 端口文件设置为8080 #Listen 12.34.56.78:8080Listen 8080

  8. CF552E 字符串 表达式求值

    http://codeforces.com/contest/552/problem/E E. Vanya and Brackets time limit per test 1 second memor ...

  9. xml publisher根据条件显示或隐藏列

     xml publisher根据条件显示或隐藏列 <?if@column:condition? > -- <?end if?> 样例: 依据PROJECT_FLAG标签显示 ...

  10. Hibernate对象持久化框架

    JDBC:(Java Data Base Connectivity)java数据库连接 java.sql包提供JDBC API,可通过它编写訪问数据库的程序代码.当中经常使用的接口和类包含以下内容: ...