<编译原理 - 函数绘图语言解释器(1)词法分析器 - python>
<编译原理 - 函数绘图语言解释器(1)词法分析器 - python>
背景
编译原理上机实现一个对函数绘图语言的解释器 - 用除C外的不同种语言实现
解释器分为三个实现块:
词法分析器:用于识别一条语句中的关键词是否符合预先定义的规则。
语法分析器:用来确定一条语句是否满足语法规则。
解释器:用来确定满足语法规则的句子,在意思上是否符合要求。
设计思路:
设计记号:词法分析器读取一个序列并根据构词规则把序列转化为记号流
定义一个字典:把所有符合一个模式的保留字、常量名、参数名、函数名等放进字典。字典是个数组,其元素的类型和记号的类型相同
设计程序的结构,具体见下面的代码
用Pycharm写了三个.py文件:
scannerclass.py
scannerfunc.py
scannermain.py
输入流是序列(存储在.txt文本),输出流是“字典”(一个个识别好的记号对象)
测试文本序列(1):
FOR T FROM 0 TO 2*PI STEP PI/50 DRAW(COS(t),sin(t));
测试文本序列(2):
//------------------This is zhushi!!------------------------
ORIGIN IS (100,300); // Sets the offset of the origin
ROT IS 0; // Set rotation Angle.
SCALE IS (1,1); // Set the abscissa and ordinate scale.
FOR T FROM 0 TO 200 STEP 1 DRAW (T,0); // The trajectory of the x-coordinate.
FOR T FROM 0 TO 150 STEP 1 DRAW (0,-T); // The trajectory of the y-coordinate.
FOR T FROM 0 TO 120 STEP 1 DRAW (T,-T); // The trajectory of the function f[t]=t.
```
函数绘图语言介绍
语句介绍
函数绘图源程序举例介绍
画出的图形介绍
Step 1 :scannerclass.py - 构造枚举类 记号类 符号表
from enum import Enum
import math
Token_Type = Enum('Token_Type', ('ORIGIN', 'SCALE', 'ROT', 'IS', 'TO', 'STEP', 'DRAW', 'FOR', 'FROM', #保留字
'T', #参数
'SEMICO', 'L_BRACKET','R_BRACKET','COMMA', #分隔符
'PLUS','MINUS','MUL','DIV','POWER', #运算符
'FUNC', #函数符
'CONST_ID', #常数
'NONTOKEN', #空记号
'ERRTOKEN')) #出错记号
class Tokens: #记号类
#type:记号类别
#lexeme:输入的字符串/属性
#value:常数值
#funcptr:函数指针
def __init__(self,type,lexeme,value,funcptr):
self.lexeme=lexeme
self.value=value
self.funcptr=funcptr
if type in Token_Type:
self.type = type
else:
print("Invalid type") # 后续待填充
Alphabet=dict([('PI',Tokens(Token_Type.CONST_ID,"PI",3.1415926,None)), ## 符号表
('E',Tokens(Token_Type.CONST_ID,"E",2.71828,None)), ## 左key右value
('T',Tokens(Token_Type.T,'T',0.0,None)),
('SIN',Tokens(Token_Type.FUNC,'SIN',0.0,math.sin)), # math.sin / math.sinh
('COS',Tokens(Token_Type.FUNC,'COS',0.0,math.cos)),
('TAN',Tokens(Token_Type.FUNC,'TAN',0.0,math.tan)),
('LN',Tokens(Token_Type.FUNC,'LN',0.0,math.log)),
('EXP',Tokens(Token_Type.FUNC,'EXP',0.0,math.exp)),
('SQRT',Tokens(Token_Type.FUNC,'SQRT',0.0,math.sqrt)), # 后续操作待填充
('ORIGIN',Tokens(Token_Type.ORIGIN,'ORIGIN',0.0,None)),
('SCALE',Tokens(Token_Type.SCALE,'SCALE',0.0,None)),
('ROT',Tokens(Token_Type.ROT,'ROT',0.0,None)),
('IS',Tokens(Token_Type.IS,'IS',0.0,None)),
('FOR',Tokens(Token_Type.FOR,'FOR',0.0,None)),
('FROM',Tokens(Token_Type.FROM,'FROM',0.0,None)),
('TO',Tokens(Token_Type.TO,'TO',0.0,None)),
('STEP',Tokens(Token_Type.STEP, 'STEP', 0.0, None)),
('DRAW',Tokens(Token_Type.DRAW, 'DRAW', 0.0, None))])
Step 2 :scannerfunc.py - 构造词法分析器类
import scannerclass as sc
import os
class scanner():
##——————初始化词法分析器
def __init__(self,file_name): #输入要输入字符流的文件名
self.LineNo = 0 #记录字符所在行的行号
self.TokenBuffer = '' #待识别记号缓存区
self.file_name=r'C:\Users\62473\Desktop\\'+file_name #此处根据个人情况做调整
if os.path.exists(self.file_name):
self.fp = open(self.file_name, "r") #文件指针
else:
self.fp = None
##——————关闭词法分析器
def CloseScanner(self):
if self.fp!=None:
self.fp.close()
##——————从输入流中读入一个字符
def GetChar(self):
Char = self.fp.read(1)
return Char
##——————输入流回退一个字符
def BackChar(self,Char): ## 非二进制打开方式不能直接seek目前位置回溯,所以用tell()-1方式从头跳转前一位置
if Char != '':
self.fp.seek(self.fp.tell()-1)
##——————加入字符到TokenBuffer待识别字符串中
def AddCharToString(self,Char):
self.TokenBuffer+=Char
##——————清空TokenBuffer字符串
def EmptyString(self):
self.TokenBuffer=''
##——————识别的字符串查表
def JudgeKeyToken(self):
Token=sc.Alphabet.get(self.TokenBuffer,sc.Tokens(sc.Token_Type.ERRTOKEN,self.TokenBuffer,0.0,None))
return Token
##——————获取记号
# 此函数由DFA转化而来(有必要的话可以写个模拟dfa函数)此函数输出一个记号。每调用该函数一次,仅仅获得一个记号。
# 因此,要获得源程序的所有记号,就要重复调用这个函数。上面声明的函数都被此函数调用过
# 因为没有自定义变量,所以只需要查表不需要构造其他东西
# 输出一个记号,没有输入
def GetToken(self):
Char = '' ##字符流
type = '' ##指向返回输出的Tokens对象
self.EmptyString() #清空缓冲区
while(1):
Char = self.GetChar()
if Char == '':
type = sc.Tokens(sc.Token_Type.NONTOKEN,Char,0.0,None)
return type
if Char == '\n':
self.LineNo=self.LineNo+1
if ~Char.isspace():
break
self.AddCharToString(Char) ##若不是空格、TAB、回车、文件结束符等,则先加入到记号的字符缓冲区中
if Char.isalpha():## 判断是否是英文
while(1):
Char = self.GetChar()
if Char.isalnum():
self.AddCharToString(Char)
else:
break
self.BackChar(Char)
type = self.JudgeKeyToken()
type.lexeme = self.TokenBuffer
return type
elif Char.isdigit():
while(1):
Char = self.GetChar()
if Char.isdigit():
self.AddCharToString(Char)
else:
break
if Char == '.':
self.AddCharToString(Char)
while(1):
Char = self.GetChar()
if Char.isdigit():
self.AddCharToString(Char)
else:
break
self.BackChar(Char)
type = sc.Tokens(sc.Token_Type.CONST_ID,self.TokenBuffer,float(self.TokenBuffer),None)
return type
else:
if Char == ';':
type = sc.Tokens(sc.Token_Type.SEMICO,Char,0.0,None)
elif Char == '(':
type = sc.Tokens(sc.Token_Type.L_BRACKET,Char,0.0,None)
elif Char == ')':
type = sc.Tokens(sc.Token_Type.R_BRACKET, Char, 0.0, None)
elif Char == ',':
type = sc.Tokens(sc.Token_Type.COMMA, Char, 0.0, None)
elif Char == '+':
type = sc.Tokens(sc.Token_Type.PLUS, Char, 0.0, None)
elif Char == '-': ##可能是行分割或减号
Char = self.GetChar()
if Char == '-':
while Char != '\n' and Char != '':
Char = self.GetChar()
self.BackChar(Char)
return self.GetToken()
else:
self.BackChar(Char)
type = sc.Tokens(sc.Token_Type.MINUS, '-', 0.0, None)
elif Char == '/': ##可能是注释分割或除号
Char = self.GetChar()
if Char == '/':
while Char != '\n' and Char != '':
Char = self.GetChar()
self.BackChar(Char)
return self.GetToken()
else:
self.BackChar(Char)
type = sc.Tokens(sc.Token_Type.DIV, '/', 0.0, None)
elif Char == '*':
Char = self.GetChar()
if (Char == '*'):
type = sc.Tokens(sc.Token_Type.POWER, '**', 0.0, None)
else:
self.BackChar(Char)
type = sc.Tokens(sc.Token_Type.MUL, '*', 0.0, None)
else:
type = sc.Tokens(sc.Token_Type.ERRTOKEN, Char, 0.0, None)
return type
Step 3 :scannermain.py - 完成I/O流
import scannerclass as sc
import scannerfunc as sf
file_name = 'test.txt' ##放在桌面的测试文本
scanner = sf.scanner(file_name)
if scanner.fp != None:
print(' 记号类别 字符串 常数值 函数指针\n')
print('——————————————————————')
while(1):
token = scanner.GetToken() #输出一个记号
if token.type == sc.Token_Type.ERRTOKEN: ##优化空格
#记号的类别不是错误或者空格,就打印出他的内容
continue
elif token.type != sc.Token_Type.NONTOKEN: ## 到了语法分析时这块需要改成ERRTOKEN,因为需要输出NONTOKEN
print("{:20s},{:>12s},{:12f},{}".format(token.type, token.lexeme,token.value,token.funcptr))
else:
break ## 文件结束符直接跳下一行读取数据放在语法分析器里面完成之前的bug
else:
print('Open Error!')
实现结果
对于测试文本(1)
FOR T FROM 0 TO 2*PI STEP PI/50 DRAW(COS(t),sin(t));
的测试运行结果如下:
换一组测试文本(2)进行的测试运行结果如下:
<编译原理 - 函数绘图语言解释器(1)词法分析器 - python>的更多相关文章
- 简单物联网:外网访问内网路由器下树莓派Flask服务器
最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...
- 利用ssh反向代理以及autossh实现从外网连接内网服务器
前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...
- 外网访问内网Docker容器
外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...
- 外网访问内网SpringBoot
外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...
- 外网访问内网Elasticsearch WEB
外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...
- 怎样从外网访问内网Rails
外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...
- 怎样从外网访问内网Memcached数据库
外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...
- 怎样从外网访问内网CouchDB数据库
外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...
- 怎样从外网访问内网DB2数据库
外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...
- 怎样从外网访问内网OpenLDAP数据库
外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...
随机推荐
- node项目的基本目录结构
1.public目录: 项目公共目录,存放静态资源(img.js.css)和公共资源,404错误提示页面: 2.routor目录: 路由控制器目录,存放路由文件,将所有的业务逻辑都都写在这里: 3.v ...
- 给idea设置默认使用的maven配置
一,前言 大家都知道,java开发中最经常使用的开发工具是Maven,最近看新同事在使用idea,我也下载了一个,准备尝试一下. 而maven是非诚方便进行工程管理的,至少管理jar包,是非常方便的, ...
- js中的计时器事件`setTimeout()` 和 `setInterval()`
js中的计时器事件 在js中,通常会有一些事件,我们需要让它 间隔一段时间之后再发生,或者 每隔一段时间 发生一次,那就需要用到我们js中的计时事件 计时事件主要有两种: setTimeout() - ...
- 4款黑科技级别的宝藏APP,能够轻松满足你的多种需求,请低调收藏
有没有这样几款软件,在你每次一换新手机的时候就会立刻重新安装下来,感觉自己已经完全离不开它们?今天就来给大家分享几个非常好用的APP. 一.小羊搜搜 在生活中人人都有自己的爱好,无论你是喜欢影视.小说 ...
- 【django】form
form 组件组要功能: 生成HTML标签 验证用户数据(显示错误信息) HTML Form提交保留上次提交数据 初始化页面显示内容 1.创建Form类时,主要涉及到 [字段] 和 [插件],字段用于 ...
- GetThreadTimes获取其它线程cpu时间
http://www.cnblogs.com/eaglet/archive/2009/03/11/1408809.html 鄙视下上面的垃圾博文,纯粹忽悠人 参考文章: http://blog.kal ...
- linux 配置多个tomcat
一.安装tomcat 1.下载链接:https://tomcat.apache.org/download-70.cgi,选择需要的版本下载(.tar.gz文件后缀) 2.通过Xshell.Xftp上传 ...
- Python采集VIP收费QQ音乐,一起来听周董最新的《说好不哭》,省3块不香吗?
环境: windows python3.6.5 模块: requests selenium json re urllib 环境与模块介绍完毕后,就可以来实行我们的操作了. 第1步: 通过一个解析网站: ...
- IPv6系列-彻底弄明白有状态与无状态配置IPv6地址
深入研究自动分配IPv6地址的Stateless(无状态)与Stateful(有状态)方式 小慢哥的原创文章,欢迎转载 目录 ▪ 一. Link-Local Address的生成方式 ▪ 二. Glo ...
- Python列表的深度排序
实例1:>>>L = [2,3,1,4]>>>L.sort()>>>L>>>[1,2,3,4] 实例2:>>> ...