编译原理——Python实现C语言词法分析器
程序采用pyside2进行交互界面设计,需要下载相应的库
- 运行效果
- 代码
from PySide2.QtWidgets import QApplication, QMessageBox, QFileDialog
from PySide2.QtUiTools import QUiLoader
import os
class Main:
#c语言关键字,运算符,界符
key_word=['auto','break','case','char','const','continue','default','do',
'double','else','enum','extern','float','for','goto','if',
'int','long','register','return','short','signed','sizeof','static',
'struct','switch','typedef','union','unsigned','void','volatile','while']
delimiters = ['{', '}', '[', ']', '(', ')', '.', ',', ':', ';']
operator = ['+', '-', '*', '/',':=', '<', '<>', '<=', '>', '>=', '=', '==','&','++','--']
#词法错误计数
error_count = 0
len_key = len(key_word)
len_del = len(delimiters)
len_ope = len(operator)
#存放分析所有结果
all_list = []
def __init__(self):
# 从文件中加载UI定义
# 从 UI 定义中动态 创建一个相应的窗口对象
# 注意:里面的控件对象也成为窗口对象的属性了
# 比如 self.ui.button , self.ui.textEdit
self.ui = QUiLoader().load('main.ui')
#多行文本框提示内容
self.ui.starttxt.setPlaceholderText('请在这里输入代码')
# 为菜单创建链接
self.ui.open.triggered.connect(self.openFile)
self.ui.save.triggered.connect(self.saveFile)
self.ui.exit.triggered.connect(self.exitFile)
self.ui.allow_ed.triggered.connect(self.allow_ed)
self.ui.refuse_ed.triggered.connect(self.refuse_ed)
self.ui.words_an.triggered.connect(self.words_an)
# self.ui.nfa.triggered.connect(self.NFA)
def openFile(self):#打开文件
# 选中文件,返回路径
filePath = QFileDialog.getOpenFileName(self.ui, "选择文件路径")
#若打开文件位置为空,返回
if filePath[0]=='':
return
else:
f = open(filePath[0], 'r', encoding='utf8')
message=f.read()
f.close()
# 将读取文件内容放入多行文本框
self.ui.starttxt.setPlainText(message)
def saveFile(self):#保存文件
ans1= self.ui.end1.toPlainText()
ans2= self.ui.end2.toPlainText()
# 返回文件存储路径
dirpath = QFileDialog.getSaveFileName(self.ui,'文件存储路径')
#若存储文件位置为空
if dirpath[0]=="":
return
else:
f =open(dirpath[0],'w+',encoding='utf8')
f.write(ans1)
f.write('\n'+ans2)
f.close()
def exitFile(self):
exit(app.exec_())
def allow_ed(self):
self.ui.starttxt.setEnabled(True)
def refuse_ed(self):
self.ui.starttxt.setEnabled(False)
# def NFA(self):
# os.system("python NFA.py")
def words_an(self):
#读取内容到text
text = self.ui.starttxt.toPlainText()
#在词法分析之前将上次词法分析的内容清空
self.all_list.clear()
self.ui.end1.clear()
self.ui.end2.clear()
for line in text.splitlines():
word = ''
word_line = []
i = 0
#循环将字符串读入word,遇到界符或者运算符停下开始判断
while i < len(line):
word += line[i]
if line[i] == ' ' or line[i] in self.delimiters or line[i] in self.operator:
#当word以英文字母,‘$’,‘_’开头时,判断是关键字还是标识符
if word[0].isalpha() or word[0] == '$' or word[0] == '_':
word = word[:-1]
if word in self.key_word:
# 关键字
# 将关键字及关键字在关键字列表中的位置以字典形式存放在word_line列表中
word_line.append({word: self.key_word.index(word)})
else:
# 标识符
# 标识符位置置零同样以字典形式存放在word_line中
word_line.append({word: 0})
# 常数
elif word[:-1].isdigit():
#常数中出现小数点,进行小数的判断
sign =0
if line[i]=='.':
i =i+1
word2 =''
#从小数点后循环读入,直至遇到空格,运算符或界符停止,小数部分读完
while True:
word2+=line[i]
#当小数点后仍然出现小数点时,此时应该出现错误
if line[i]=='.':
sign =1
i=i+1
continue
if line[i] == ' ' or line[i] in self.delimiters or line[i] in self.operator:
word =word+word2
break
i =i+1
if sign==0:
#将小数以字典形式加入列表
word_line.append({word[:-1]: -1})
else:
word_line.append({word[:-1]: -3})#错误的小数
else:
word_line.append({word[:-1]: -1})
# 不合法标识符
elif word[:-1]!='':
word_line.append({word[:-1]:-2})
self.error_count = self.error_count+1
# 字符是界符
if line[i] in self.delimiters:
word_line.append({line[i]: self.len_key + self.delimiters.index(line[i])})
# 字符是运算符
elif line[i] in self.operator:
s = line[i] + line[i + 1]
#两位运算符
if s in self.operator:
word_line.append({s: self.len_key + self.len_del + self.operator.index(s)})
i += 1
#单位运算符
else:
word_line.append({line[i]: self.len_key + self.len_del + self.operator.index(line[i])})
#每判断一个单词,word置为空
word = ''
i += 1
self.all_list.append(word_line)#将单词信息存放在word_line列表中
#将判断的信息打印在控制台
for i in range(len(self.all_list)):
print(self.all_list[i])
self.show()
def circle(self):
pass
def show(self):
self.ui.end1.append('-----------------------------------------------')
self.ui.end1.append('行号'+' '+'单词'+' '+'类型'+' '+'是否合法'+' '+'单词码'+' ')
self.ui.end1.append('-----------------------------------------------')
self.ui.end2.append(str(self.error_count)+' errors')
#显示完毕后,错误计数归零
self.error_count = 0
self.ui.end2.append('-----------------------------------------------')
#循环遍历列表,判断每个单词的类型和是否合法
for i in range(len(self.all_list)):
l = len(self.all_list[i])
for j in range(l):
leixing = ''
hefa = ''
#number为存储单词的单词码,words为该单词
number =list(self.all_list[i][j].values())[0]
words =list(self.all_list[i][j].keys())[0]
if 0< number <=self.len_key:
leixing = '保留字'
hefa ='合法'
elif self.len_key< number <=self.len_key+self.len_del:
leixing = '界符'
hefa = '合法'
elif self.len_key+self.len_del< number <=self.len_key+self.len_del+self.len_ope:
leixing = '运算符'
hefa = '合法'
elif number==0:
leixing ='标识符'
hefa = '合法'
elif number==-1:
leixing = '常数'
hefa = '合法'
elif number==-2:
leixing ='标识符'
hefa ='不合法'
elif number==-3:
leixing ='常数'
hefa ='不合法'
if hefa =='合法':
self.ui.end1.append("{:<6}{:<6}{:<8}{:<7}{:<5}".format(i+1,words,leixing,hefa,number))
else:
self.ui.end1.append("{:<6}{:<6}{:<8}{:<7}{:<5}{}".format(i+1, words, leixing, hefa, number,'<-错误行'))
if number==-2:
self.ui.end2.append("{:<6}{:<6}{:<10}".format(i+1,words,'标识符命名错误'))
elif number==-3:
self.ui.end2.append("{:<6}{:<6}{:<10}".format(i + 1, words, '常数命名错误'))
app = QApplication([])
start = Main()
start.ui.show()
app.exec_()