一个简单的语义分析算法:单步算法——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网上看到一个简单的算法题目,虽然简单,但是如何完成一段高效.简洁.让人容易看懂的代码对于我这个基础不好,刚刚进入计算机行业的小白来说还是有意义的.而且在写代码的过程中,会发现自 ...
随机推荐
- tcp流式套接字和udp数据报套接字编程区别
1. 流式套接字采用字节流方式进行传输,而数据报套接字 使用数据报形式传输数据2. tcp套接字会产生粘包,udp有消息边界,不会形成粘包.3. tcp编程可以保证消息的完整性,udp则不能保证4. ...
- Hypergraph Models超图模型
最近看了篇Paper(Hyperspectral Image Classification Through Bilayer Graph-Based Learning),里面出现了一个超图(Hyperg ...
- redis使用入门
redis使用 1. redis安装 sudo apt-get install redis-server 2. redis使用(以localhost为例) redis启动 redis-cli -h h ...
- selenium-java,UI自动化截图方法
截图方法: import java.io.File; import java.io.IOException; import org.apache.commons.io.FileUtils; impor ...
- 二次剩余-Cipolla
二次剩余 \(Cipolla\) 算法 概述 大概就是在模 \(p\) 意义下开根号,如求解方程\(x^2\equiv n(mod\ p)\). 这里只考虑 \(p\) 为素数的情况.若 \(p=2\ ...
- MEF学习总结(3)---Attribute Model Programing
上一片介绍了Primitive层,Attribute Model可以认为是对Primitive的上层实现.主要包括如下内容: 1. 一系列的Attribute来定义Import和Export 常用的有 ...
- Android getevent用法详解
getevent 指令用于获取 input 输入事件,比如获取按键上报信息.获取触摸屏上报信息等. 指令源码路径:/system/core/toolbox/getevent.c getevent -h ...
- 在同一服务器使用git分支建立线上 和 测试 项目
分支分配文件夹后 sourcetree 创建分支与合并 https://blog.csdn.net/qq_34975710/article/details/74469068 线上分支master 测试 ...
- postman 请求 页面出现 Could not get any response 解决方法
1.关闭掉证书 2.再关闭掉代理 3.去掉请求头的Content-Length字段 (或者一个个试请求头的值,看去掉那些就请求成功了) 后记: 网友 @ 重庆张晓祥 提供了个线索确实让我想起以前我从浏 ...
- java web 程序---猜数字游戏
思路:1.第一个是随机产生的数字,告诉我们去猜 cai.jsp 2.第二个是一个form表单,提交按钮后,将连接到验证页面 test1.jsp 3.第三个是比较猜的数和随机数.对了,提示再玩一次,不 ...