<编译原理 - 函数绘图语言解释器(1)词法分析器 - python>
背景
-
编译原理上机实现一个对函数绘图语言的解释器 - 用除C外的不一样种语言实现git
-
解释器分为三个实现块:数组
-
词法分析器:用于识别一条语句中的关键词是否符合预先定义的规则。缓存
-
语法分析器:用来肯定一条语句是否知足语法规则。函数
-
解释器:用来肯定知足语法规则的句子,在乎思上是否符合要求。测试
-
-
设计思路:优化
-
设计记号:词法分析器读取一个序列并根据构词规则把序列转化为记号流spa
-
定义一个字典:把全部符合一个模式的保留字、常量名、参数名、函数名等放进字典。字典是个数组,其元素的类型和记号的类型相同设计
-
设计程序的结构,具体见下面的代码
-
-
用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)进行的测试运行结果以下:
原文出处:https://www.cnblogs.com/ymjun/p/11773681.html