一个简单的语义分析算法:单步算法——Python实现
以前 曾经有一个人教会我一件事
要学会相信一些看似不可能的事
当你真的相信的时候
或许 没有什么事情是不可能的
——《秦时明月•与子同归》
在编译原理的众多书籍中,陈述了很多生成语法树的经典算法,它们大多是基于递归的方式进行工作的。在本文中,将与大家分享一种基于迭代方式的、易于理解的语法树生成算法,由于其一次成功迭代仅生成一个语法“树枝”的处理特点,可称之为单步算法。
我们将通过对中缀表达式的语法树生成实验进行算法细节的初步阐述与验证。
第一阶段:简单模型
我们第一阶段的中缀表达式语法规定如下:
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实现的更多相关文章
- 一个简单的多机器人编队算法实现--PID
用PID进行领航跟随法机器人编队控制 课题2:多机器人编队控制研究对象:两轮差动的移动机器人或车式移动机器人研究内容:平坦地形,编队的保持和避障,以及避障和队形切换算法等:起伏地形,还要考虑地形情况对 ...
- 实现一个简单的邮箱地址爬虫(python)
我经常收到关于email爬虫的问题.有迹象表明那些想从网页上抓取联系方式的人对这个问题很感兴趣.在这篇文章里,我想演示一下如何使用python实现一个简单的邮箱爬虫.这个爬虫很简单,但从这个例子中你可 ...
- 通过创建一个简单的骰子游戏来探究 Python
在我的这系列的第一篇文章 中, 我已经讲解如何使用 Python 创建一个简单的.基于文本的骰子游戏.这次,我将展示如何使用 Python 模块 Pygame 来创建一个图形化游戏.它将需要几篇文章才 ...
- 一个简单文本分类任务-EM算法-R语言
一.问题介绍 概率分布模型中,有时只含有可观测变量,如单硬币投掷模型,对于每个测试样例,硬币最终是正面还是反面是可以观测的.而有时还含有不可观测变量,如三硬币投掷模型.问题这样描述,首先投掷硬币A,如 ...
- 【bzoj5016】[Snoi2017]一个简单的询问 莫队算法
题目描述 给你一个长度为N的序列ai,1≤i≤N和q组询问,每组询问读入l1,r1,l2,r2,需输出 get(l,r,x)表示计算区间[l,r]中,数字x出现了多少次. 输入 第一行,一个数字N,表 ...
- 通过编写一个简单的漏洞扫描程序学习Python基本语句
今天开始读<Python绝技:运用Python成为顶级黑客>一书,第一章用一个小例子来讲解Python的基本语法和语句.主要学习的内容有:1. 安装第三方库.2. 变量.字符串.列表.词典 ...
- 一个简单的mock server
在前后端分离的项目中, 前端无需等后端接口提供了才调试, 后端无需等第三方接口提供了才调试, 基于“契约”,可以通过mock server实现调试, 下面是一个简单的mock server,通过pyt ...
- Adaboost算法的一个简单实现——基于《统计学习方法(李航)》第八章
最近阅读了李航的<统计学习方法(第二版)>,对AdaBoost算法进行了学习. 在第八章的8.1.3小节中,举了一个具体的算法计算实例.美中不足的是书上只给出了数值解,这里用代码将它实现一 ...
- 一个简单算法题引发的思考<DNA sorting>(about cin/template/new etc)
首先是昨天在北京大学oj网上看到一个简单的算法题目,虽然简单,但是如何完成一段高效.简洁.让人容易看懂的代码对于我这个基础不好,刚刚进入计算机行业的小白来说还是有意义的.而且在写代码的过程中,会发现自 ...
随机推荐
- JS实现浏览器打印、打印预览
1.JS实现打印的方式方式一:window.print()window.print();会弹出打印对话框,打印的是window.document.body.innerHTML中的内容,下面是从网上摘到 ...
- Codeforces 15E Triangles 【组合计数】
Codeforces 15E Triangles Last summer Peter was at his granny's in the country, when a wolf attacked ...
- Quartz 2D编程指南(5) - 变换(Transforms)
Quartz 2D 绘制模型定义了两种独立的坐标空间:用户空间(用于表现文档页)和设备空间(用于表现设备的本地分辨率).用户坐标空间用浮点数表示坐标,与设备空间的像素分辨率没有关系.当我们需要一个点或 ...
- hadoop常见错误汇总及解决办法一
我们经常会遇到一些问题,而且可能会重复性遇到,这些方案可以收藏为以后备用.我们经常遇到如下问题:1.两次以上格式化造成NameNode 和 DataNode namespaceID 不一致,有几种解决 ...
- 《DSP using MATLAB》示例 Example 6.14、6.15
- bat命令1
echo 命令 打开回显或关闭请求回显功能,或显示消息.如果没有任何参数,echo命令将显示当前回显设置. 语法 echo [{on|off}] [message] Sample:@echo off ...
- Toxiproxy 网络情况模式代理
1. 介绍 Toxiproxy is a framework for simulating network conditions. It's made specifically to work in ...
- MySQL的安装配置教程
1. 官网下载ZIP压缩版本(本人电脑是64位的) x64bit MySQL Community 2. 解压到E:\SoftwareFiles\mysql-5.7.11-winx64 3. 在E:\S ...
- bzoj 3671 [Noi2014]随机数生成器——贪心(时间复杂度分配)
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3671 设 x 为一个点的行号, y 为一个点的列号:原本想着判断一个点能不能选就是看选了的点 ...
- jeecg中List页面标签的用法
1.t:datagrid的常用属性 1. <t:datagrid name="jeecgDemoList" checkbox="true" sortNam ...