最近在学习编译原理。由于实验要求有词法分析器,这里我就先记录一下词法分析器实现过程以及具体思路。
目标语言此处我选择的目标语言是c语言的子集来进行词法分析。
实现语言此处我选用的语言是python,主要还是考虑到python的数据结构比较强大而且包容性强。并且我pyqt用的比较熟练,很容易设计出GUI界面。关于pyqt的相关内容网上资料比较少对初学者不是很友好,我下面会出一些关于pyqt的教程,还望持续关注!
词法分析器主要工作从源程序文件中读入字符统计行数和列数用于进行错误定位识别出单词并用(内码,属性)二元式表示识别出错误记录报告错误但不会终止扫描填写标识符表 设计思路
词法分析不必要设计成单独的一遍,我认为词法分析器应该设计成一个子程序,每当语法分析需要一个单词符号时,那么此时向词法分析器传递一个输入串,词法分析器便要能分析出这个输入串中的单词。
设计流程图 算法思路对于单词的分析关键在于第一个字符的性质。第一个字符的性质决定了下面的单词分析进程。如果第一个字符是一个数字那么下面这个单词就要判断是否为常量。接下来读取的如果是字符除了是e或E其他字符都可以直接判断此单词非法为error。因此这里可以将其单独分离出一个函数,这里我取名为isDigit()函数。其他包括标识符的判定以及算术或逻辑运算符的判定也可以按照此思路分离出相应的函数。
函数表
这里分为四种情况
// 判断数字代码
def IsDigits(self, inString, pos):
flag = False
for i in inString:
pos += 1
if i.isdigit():
self.token += str(i)
flag = True
elif i == '.' and i not in self.token and 'e' not in self.token and 'E' not in self.token:
self.token += str(i)
elif i == 'e' or i == 'E' and i not in self.token: # 处理含E或e的合法指数情况
self.token += str(i)
else:
if i in self.operatorOrDelimiter or i == ' ' or i == 'n':
flag = True
else:
flag = False
break
return flag, pos
对应的scan()函数中处理数字开头的代码
// An highlighted block
if inString[0].isdigit():
judge, index = self.IsDigits(inString, 0)
if judge:
"""if '.' in self.token: # 此处是对常量的转化过程此处写成注释
print("--------")
print(float(self.token))
print("--------")
elif 'e' not in self.token and 'E' not in self.token:
print("--------")
print(int(self.token))
print("--------")
else:
num1 = 0
num2 = 0
if 'E' in self.token:
l = self.token.split('E')
if '.' in l[0]:
num1 = float(l[0])
else:
num1 = int(l[0])
num2 = int(l[1])
elif 'e' in self.token:
l = self.token.split('e')
if '.' in l[0]:
num1 = float(l[0])
else:
num1 = int(l[0])
num2 = int(l[1])
for i in range(0, num2):
num1 *= 10
print("--------")
print(num1)
print("--------")"""
self.result.append([self.token, "常数", (row, col)])
else:
self.result.append([self.token, "ERROR", (row, col)])
if index <= len(inString) and inString[index-1] != ' ':
self.scan(inString[index - 1:], row, col)
处理以字母开头的字符串
此处以字母开头的字符串可能出现的情况为:
标识符关键字非法字符串 对于这里的判断要注意几种情况:
类似i++这种一个标识符后面跟着算术或逻辑运算符i;后面跟着终结符这种比较好判断单独的一个标识符或关键字 如 int这种。 部分代码
def isReserve(self, target): # 判断是否为关键字
if target in self.reserveWord:
return True
return False
def isMark(self, inString, pos):
flag = False
for i in inString:
pos += 1
if i.isalpha() or i.isdigit() or i == '_':
self.token += str(i)
flag = True
elif i in self.operatorOrDelimiter: # 遇到算术/逻辑/分隔符结束搜索
flag = True
break
else:
flag = False
return flag, pos
对应scan()中以字母开头部分代码
elif inString[0].isalpha():
judge, index = self.isMark(inString, 0)
if self.isReserve(self.token):
self.result.append([self.token, "关键字", (row, col)])
else:
self.result.append([self.token, "标识符", (row, col)])
if index <= len(inString) and not inString[index-1].isalpha(): # 此处主要是处理陷入死循环的情况,因为只有最后一个字符不是字母的时候才需继续
self.scan(inString[index-1:], row, col)
处理一些算术/逻辑运算符的情况
这里主要是考虑如下一些情况
运算符在开头这里也是有可能的比如 ++i;这在c以及c++中均为合法语句,词法分析器应该能够识别出这一个串中的++、i单词运算符在内部,这也是有可能的比如a+b,词法分析器也要识别出a、+、b这几个单词 部分代码
def IsOperation(self, inString, pos):
flag = False
if len(inString) == 1:
self.token += str(inString[0])
return True, pos
for i in inString:
pos += 1
if i in self.operatorOrDelimiter:
self.token += str(i)
else:
break
if self.token in self.operatorOrDelimiter:
return True, pos
return False, pos
scan()函数部分代码
elif inString[0] in self.operatorOrDelimiter:
judge, index = self.IsOperation(inString, 0)
self.result.append([self.token, "操作符或分隔符", (row, col)])
if index < len(inString) and index - 1 >= 0: # 主要是考虑当只有一个字符的情况,因为我在判断操作符时,当首先判断出其长度为一时会默认是操作符,并不再进行判断,导致会出现死循环
self.scan(inString[index - 1:], row, col)
至此一些关键处理代码已经实现,并经过测试后是可以使用的,上面代码部分只是我的思路,我使用了递归调用,这会有一个缺点就是无法更好的得出当前处理的位置,仍需改进。
运行测试图片
给您的建议 各位看到这里应该对我的思路有了一定的认识,但我的水平比较低,而且表达能力不是很强,如果有哪些不懂得地方,欢迎与我联系。我希望各位能够亲自动手实现,其实并不是很难。我使用了递归来进行解析,其实并不是一定要使用,而且递归只会徒增程序得复杂性。我进行了许多边界测试,但仍不是很全。我看来,在设计时没必要考虑过多得边界问题,等我们程序大致框架搭起来了以后,通过运行测试边界,然后再进行更改调试效果会更好。由于我们编译原理课程实验还未验收此处我就不把完整代码放出但我把截图放在下方,希望大家可以对照参考同时也欢迎私信我交流讨论。
如果您想要完整代码,欢迎私信我并说明用途,感谢您的支持!
== 我还会持续更新包括GUI界面内容欢迎持续关注~~ ==



