以前 曾经有一个人教会我一件事

要学会相信一些看似不可能的事

 当你真的相信的时候

 或许 没有什么事情是不可能的

——《秦时明月•与子同归》

在编译原理的众多书籍中,陈述了很多生成语法树的经典算法,它们大多是基于递归的方式进行工作的。在本文中,将与大家分享一种基于迭代方式的、易于理解的语法树生成算法,由于其一次成功迭代仅生成一个语法“树枝”的处理特点,可称之为单步算法。

我们将通过对中缀表达式的语法树生成实验进行算法细节的初步阐述与验证。

第一阶段:简单模型

我们第一阶段的中缀表达式语法规定如下:

expr   ->  expr addop term | term
addop -> + | -
term -> term mulop factor | factor
mulop -> * | / | %
factor -> var | num | function

该语法的等价描述:

运算符优先级:(从高到低)
——————————————————————————
* / % 二元操作符
- + 二元操作符
——————————————————————————
参与运算的可以是:
——————————————————————————
num:常数
var:变量
function:函数
expr:表达式
——————————————————————————

对于这种简单的中缀表达式,我们的算法处理过程如下:(先大概看一遍处理过程,不要阻塞在这儿)

#例子1:
90-565*235/31+6756+45/312-321%12
90-A/31+6756+45/312-321%12
90-A+6756+45/312-321%12
A+6756+45/312-321%12
A+45/312-321%12
A+A-321%12
A-321%12
A-A
A

为了接下来讨论方便,我们做一些类似正则表达式的符号约定:

# ^ 行首锚定
# $ 行尾锚定
# ... 与正则的(.*)意义相同,表示任意多字符
# A 参与运算的单位 可以是num、var、function或者expr
# 下面便是我们算法的合并规则
^A-A$
^A-A+...
^A-A-...
^A+A$
^A+A-...
^A+A+...
...A*A...
...A/A...
...A%A...

算法处理过程:

下面我们故意给中缀表达式加入一语法错误,看一看处理过程:

如果语法正确,是不可能上图情况的,所以,如果出现上图情况,可断定所给中缀表达式中有语法错误出现。

下面我们给出此算法的Python实现代码:

 # 数据结构
"""
list structure :
- + * / % ( ) @var @num @expr @func ["-",None]
["+",None]
["*",None]
["/",None]
["%",None]
["(",None]
[")",None]
["@var","varName"]
["@num","num_string"]
["@expr","-"|"+"|"*"|"/"|"%",listPtr,...]
["@func","funcName",listPtr1,...] """ ''' 31 + 8 * 9 '''
listToParse=[ ['@num',''] , ['+',None] , ['@num',''] , ['*',None] , ['@num',''] ]

运算符'-'规则:

 ########### return value :
############# 0 parsed some expresions
############# 1 done nothing but no errors happene
def module_minus(lis,i): # left i right are both indexes :)
left=i-1
right=i+1 # process: ^A-A$
if i==1 and len(lis)==3:
if lis[left][0]=="@var" or lis[left][0]=="@num" or \
lis[left][0]=="@expr" or lis[left][0]=="@func" :
if lis[right][0]=="@var" or lis[right][0]=="@num" or \
lis[right][0]=="@expr" or lis[right][0]=="@func" :
leftPtr=lis[left]
rightPtr=lis[right]
del lis[left:left+3]
lis.insert(left,["@expr","-",leftPtr,rightPtr])
return 0
# process: ^A-A+... | ^A-A-...
if i==1 and len(lis)>3:
if lis[left][0]=="@var" or lis[left][0]=="@num" or \
lis[left][0]=="@expr" or lis[left][0]=="@func" :
if lis[right][0]=="@var" or lis[right][0]=="@num" or \
lis[right][0]=="@expr" or lis[right][0]=="@func" :
if lis[right+1][0] in "-+" :
leftPtr=lis[left]
rightPtr=lis[right]
del lis[left:left+3]
lis.insert(left,["@expr","-",leftPtr,rightPtr])
return 0 return 1

运算符'+'规则:

 ########### return value :
############# 0 parsed some expresions
############# 1 done nothing but no errors happene
def module_plus(lis,i): # left i right are both indexes :)
left=i-1
right=i+1 # process ^A+A$
if i==1 and len(lis)==3:
if lis[left][0]=="@var" or lis[left][0]=="@num" or \
lis[left][0]=="@expr" or lis[left][0]=="@func" :
if lis[right][0]=="@var" or lis[right][0]=="@num" or \
lis[right][0]=="@expr" or lis[right][0]=="@func" :
leftPtr=lis[left]
rightPtr=lis[right]
del lis[left:left+3]
lis.insert(left,["@expr","+",leftPtr,rightPtr])
return 0
# process ^A+A-... | ^A+A+...
if i==1 and len(lis)>3:
if lis[left][0]=="@var" or lis[left][0]=="@num" or \
lis[left][0]=="@expr" or lis[left][0]=="@func" :
if lis[right][0]=="@var" or lis[right][0]=="@num" or \
lis[right][0]=="@expr" or lis[right][0]=="@func" :
if lis[right+1][0] in "-+" :
leftPtr=lis[left]
rightPtr=lis[right]
del lis[left:left+3]
lis.insert(left,["@expr","+",leftPtr,rightPtr])
return 0 return 1

运算符'*'规则:

 ########### return value :
############# 0 parsed some expresions
############# 1 done nothing but no errors happene
def module_multiply(lis,i): # left i right are both indexes :)
left=i-1
right=i+1 # i is not at head and end
# process ...A*A...
if i<len(lis)-1 and i>0 :
if lis[left][0]=="@var" or lis[left][0]=="@num" or \
lis[left][0]=="@expr" or lis[left][0]=="@func" :
if lis[right][0]=="@var" or lis[right][0]=="@num" or \
lis[right][0]=="@expr" or lis[right][0]=="@func" :
leftPtr=lis[left]
rightPtr=lis[right]
del lis[left:left+3]
lis.insert(left,["@expr","*",leftPtr,rightPtr])
return 0
return 1

运算符'/'规则:

 ########### return value :
############# 0 parsed some expresions
############# 1 done nothing but no errors happene
def module_division(lis,i): # left i right are both indexes :)
left=i-1
right=i+1 # i is not at head and end
# process ...A/A...
if i<len(lis)-1 and i>0 :
if lis[left][0]=="@var" or lis[left][0]=="@num" or \
lis[left][0]=="@expr" or lis[left][0]=="@func" :
if lis[right][0]=="@var" or lis[right][0]=="@num" or \
lis[right][0]=="@expr" or lis[right][0]=="@func" :
leftPtr=lis[left]
rightPtr=lis[right]
del lis[left:left+3]
lis.insert(left,["@expr","/",leftPtr,rightPtr])
return 0
return 1

运算符'%'规则:

 ########### return value :
############# 0 parsed some expresions
############# 1 done nothing but no errors happene
def module_mod(lis,i): # left i right are both indexes :)
left=i-1
right=i+1 # i is not at head and end
# process ...A%A...
if i<len(lis)-1 and i>0 :
if lis[left][0]=="@var" or lis[left][0]=="@num" or \
lis[left][0]=="@expr" or lis[left][0]=="@func" :
if lis[right][0]=="@var" or lis[right][0]=="@num" or \
lis[right][0]=="@expr" or lis[right][0]=="@func" :
leftPtr=lis[left]
rightPtr=lis[right]
del lis[left:left+3]
lis.insert(left,["@expr","%",leftPtr,rightPtr])
return 0
return 1

语义分析函数:

 ########### return value :
############# 0 parsed sucessfully
############# 1 syntax error
############# 2 list is null :(
def parse_simple_expr(lis):
while 1:
if len(lis)==0 :
return 2
if len(lis)==1 :
if lis[0][0]=="@var" or lis[0][0]=="@num" or \
lis[0][0]=="@expr" or lis[0][0]=="@func" :
return 0
else:
return 1
if len(lis)==2 :
return 1
i=0
while 1:
if i<0 or i>=len(lis):
return 1
if lis[i][0] in "-+*/%":
if lis[i][0]=="-":
if module_minus(lis,i)==0:
break
else:
i=i+1
continue
elif lis[i][0]=="+":
if module_plus(lis,i)==0:
break
else:
i=i+1
continue
elif lis[i][0]=="*":
if module_multiply(lis,i)==0:
break
else:
i=i+1
continue
elif lis[i][0]=="/":
if module_division(lis,i)==0:
break
else:
i=i+1
continue
elif lis[i][0]=="%":
if module_mod(lis,i)==0:
break
else:
i=i+1
continue
else :
return 1
else:
i=i+1 return 0

实验结果:

第二阶段:为我们的中缀表达式加入括号 :)

新的文法规则如下:

expr   -> expr addop term | term
addop -> + | -
term -> term mulop factor | factor
mulop -> * | / | %
factor -> ( expr )| var | num | function

算法处理大概过程:

寻找出表达式中第一对'('')'位置的方法:

 ########### return value :[intStatusCode,indexOf'(',indexOf')']
############# intStatusCode
############# 0 sucessfully
############# 1 no parenthesis matched
############# 2 list is null :(
def module_parenthesis_place(lis):
length=len(lis)
err=0
x=0
y=0 if length==0:
return [2,None,None] try:
x=lis.index([")",None])
except:
err=1
lis.reverse()
try:
y=lis.index(["(",None],length-x-1)
except:
err=1
lis.reverse()
y=length-y-1 if err==1:
return [1,None,None]
else:
return [0,y,x]

处理包含有'('')'功能的中缀表达式的方法:

########### return value :
############# 0 parsed sucessfully
############# 1 syntax error
############# 2 list is null :(
def parse_parenthesis_expr(lis):
while 1:
if len(lis)==0 :
return 2
if len(lis)==1 :
if lis[0][0]=="@var" or lis[0][0]=="@num" or \
lis[0][0]=="@expr" or lis[0][0]=="@func" :
return 0
else:
return 1
if len(lis)==2 :
return 1
place=module_parenthesis_place(lis)
if place[0]==0:
mirror=lis[(place[1]+1):place[2]]
if parse_simple_expr(mirror)==0:
del lis[place[1]:(place[2]+1)]
lis.insert(place[1],mirror[0])
else:
return 1
else:
return parse_simple_expr(lis) return 0

实验结果:

附录:

在接下来的文章中,将会逐步增加其他的语义分析功能,如参与表达式运算的函数、条件语句的语法树生成等。

如有问题或者建议,欢迎留言讨论 :)

一个简单的语义分析算法:单步算法——Python实现的更多相关文章

  1. 一个简单的多机器人编队算法实现--PID

    用PID进行领航跟随法机器人编队控制 课题2:多机器人编队控制研究对象:两轮差动的移动机器人或车式移动机器人研究内容:平坦地形,编队的保持和避障,以及避障和队形切换算法等:起伏地形,还要考虑地形情况对 ...

  2. 实现一个简单的邮箱地址爬虫(python)

    我经常收到关于email爬虫的问题.有迹象表明那些想从网页上抓取联系方式的人对这个问题很感兴趣.在这篇文章里,我想演示一下如何使用python实现一个简单的邮箱爬虫.这个爬虫很简单,但从这个例子中你可 ...

  3. 通过创建一个简单的骰子游戏来探究 Python

    在我的这系列的第一篇文章 中, 我已经讲解如何使用 Python 创建一个简单的.基于文本的骰子游戏.这次,我将展示如何使用 Python 模块 Pygame 来创建一个图形化游戏.它将需要几篇文章才 ...

  4. 一个简单文本分类任务-EM算法-R语言

    一.问题介绍 概率分布模型中,有时只含有可观测变量,如单硬币投掷模型,对于每个测试样例,硬币最终是正面还是反面是可以观测的.而有时还含有不可观测变量,如三硬币投掷模型.问题这样描述,首先投掷硬币A,如 ...

  5. 【bzoj5016】[Snoi2017]一个简单的询问 莫队算法

    题目描述 给你一个长度为N的序列ai,1≤i≤N和q组询问,每组询问读入l1,r1,l2,r2,需输出 get(l,r,x)表示计算区间[l,r]中,数字x出现了多少次. 输入 第一行,一个数字N,表 ...

  6. 通过编写一个简单的漏洞扫描程序学习Python基本语句

    今天开始读<Python绝技:运用Python成为顶级黑客>一书,第一章用一个小例子来讲解Python的基本语法和语句.主要学习的内容有:1. 安装第三方库.2. 变量.字符串.列表.词典 ...

  7. 一个简单的mock server

    在前后端分离的项目中, 前端无需等后端接口提供了才调试, 后端无需等第三方接口提供了才调试, 基于“契约”,可以通过mock server实现调试, 下面是一个简单的mock server,通过pyt ...

  8. Adaboost算法的一个简单实现——基于《统计学习方法(李航)》第八章

    最近阅读了李航的<统计学习方法(第二版)>,对AdaBoost算法进行了学习. 在第八章的8.1.3小节中,举了一个具体的算法计算实例.美中不足的是书上只给出了数值解,这里用代码将它实现一 ...

  9. 一个简单算法题引发的思考<DNA sorting>(about cin/template/new etc)

    首先是昨天在北京大学oj网上看到一个简单的算法题目,虽然简单,但是如何完成一段高效.简洁.让人容易看懂的代码对于我这个基础不好,刚刚进入计算机行业的小白来说还是有意义的.而且在写代码的过程中,会发现自 ...

随机推荐

  1. 最新博客开启 - Noogle's Blogs

    博客地址: http://noogel.xyz/ 戳我进入 Noogle's Blogs

  2. HDU1496 Equations 卡时间第二题

    Consider equations having the following form: a*x1^2+b*x2^2+c*x3^2+d*x4^2=0 a, b, c, d are integers ...

  3. linux自学(四)之开始centos学习,网络配置

    上一篇:linux自学(三)之开启虚拟机 安装好镜像之后,重启之后需要登录,我这里直接是root账号直接登录的,注意:输入密码的时候不显示. 之后输入ifconfig最常用的命令来查看网卡信息,出现c ...

  4. bzoj 4595 激光发生器

    bzoj 4595 激光发生器 光线为射线,每次找到与当前光线相交且距离最近的镜子,然后旋转光线. 直线,射线利用线上一点+方向向量的方式表示.旋转时,旋转中心作为线上一点不变,方向向量左乘旋转矩阵. ...

  5. 记录一些WPF常用样式方便以后复用(二)(Button、CheckBox、输入账号密码框)(转)

    Button (一) <Style x:Key="ButtonSaveStyle" TargetType="{x:Type Button}"> &l ...

  6. nginx 调试

    配置单进程非daemon方式启动 daemon off; master_process off;

  7. 关于RAS加密中pfx格式提取字符串私钥 (转)

    openssl 使用以下命令 如果如果执行出错,请查看是否安装环境或在命令前面加上openssl --提取出来的无法直接使用 pkcs12 -in zhaoshang2.pfx -nocerts -n ...

  8. SharePoint无法搜索解决

    重启服务器后,站点搜索时提示错误,无法进行搜索,进入管理中心搜索管理看到,"查询处理"出错. 解决方法: 停止搜索服务,重新启动,如下图所示 重启服务后,过了几分钟重新查询,查询正 ...

  9. JSONUtils的几个常用方法

    1.首先新建1个JSONUtils类 public class JSONUtils { /** * * @author wangwei JSON工具类 * @param * */ /*** * 将Li ...

  10. PHP使用RabbitMQ

    基本概念 Broker:简单来说就是消息队列服务器实体. Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列. Queue:消息队列载体,每个消息都会被投入到一个或多个队列. Bind ...