在 python 中编写 Sebesda 的词法分析器。不适用于输入文件中的最后一个词位
Writing Sebesda's lexical analyzer in python. Does not work for last lexeme in the input file
我必须将 Sebesda 的 Concpets of Programming Languages(第 4 章第 2 节)中的代码翻译成 python。这是我目前所拥有的:
# Character classes #
LETTER = 0
DIGIT = 1
UNKNOWN = 99
# Token Codes #
INT_LIT = 10
IDENT = 11
ASSIGN_OP = 20
ADD_OP= 21
SUB_OP = 22
MULT_OP = 23
DIV_OP = 24
LEFT_PAREN = 25
RIGHT_PAREN = 26
charClass = ''
lexeme = ''
lexLen = 0
token = ''
nextToken = ''
### lookup - function to lookup operators and parentheses ###
### and return the token ###
def lookup(ch):
def left_paren():
addChar()
globals()['nextToken'] = LEFT_PAREN
def right_paren():
addChar()
globals()['nextToken'] = RIGHT_PAREN
def add():
addChar()
globals()['nextToken'] = ADD_OP
def subtract():
addChar()
globals()['nextToken'] = SUB_OP
def multiply():
addChar()
globals()['nextToken'] = MULT_OP
def divide():
addChar()
globals()['nextToken'] = DIV_OP
options = {')': right_paren, '(': left_paren, '+': add,
'-': subtract, '*': multiply , '/': divide}
if ch in options.keys():
options[ch]()
else:
addChar()
### addchar- a function to add next char to lexeme ###
def addChar():
#lexeme = globals()['lexeme']
if(len(globals()['lexeme']) <=98):
globals()['lexeme'] += nextChar
else:
print("Error. Lexeme is too long")
### getChar- a function to get the next Character of input and determine its character class ###
def getChar():
globals()['nextChar'] = globals()['contents'][0]
if nextChar.isalpha():
globals()['charClass'] = LETTER
elif nextChar.isdigit():
globals()['charClass'] = DIGIT
else:
globals()['charClass'] = UNKNOWN
globals()['contents'] = globals()['contents'][1:]
## getNonBlank() - function to call getChar() until it returns a non whitespace character ##
def getNonBlank():
while nextChar.isspace():
getChar()
## lex- simple lexical analyzer for arithmetic functions ##
def lex():
globals()['lexLen'] = 0
getNonBlank()
def letterfunc():
globals()['lexeme'] = ''
addChar()
getChar()
while(globals()['charClass'] == LETTER or globals()['charClass'] == DIGIT):
addChar()
getChar()
globals()['nextToken'] = IDENT
def digitfunc():
globals()['lexeme'] = ''
addChar()
getChar()
while(globals()['charClass'] == DIGIT):
addChar()
getChar()
globals()['nextToken'] = INT_LIT
def unknownfunc():
globals()['lexeme'] = ''
lookup(nextChar)
getChar()
lexDict = {LETTER: letterfunc, DIGIT: digitfunc, UNKNOWN: unknownfunc}
if charClass in lexDict.keys():
lexDict[charClass]()
print('The next token is: '+ str(globals()['nextToken']) + ' The next lexeme is: ' + globals()['lexeme'])
with open('input.txt') as input:
contents = input.read()
getChar()
lex()
while contents:
lex()
我使用字符串 sum + 1 / 33
作为示例输入字符串。据我了解,在顶层第一次调用 getChar() 会将 characterClass 设置为 LETTER,并将 contents
设置为 um + 1 / 33
。
程序随后进入while循环并调用lex()
。 lex()
依次将单词 sum 累加到 lexeme
中。当 letterfunc
内的 while 循环遇到第一个 white-space 字符时,它中断,退出 lex()
由于contents
不为空,程序再次在顶层进行while循环。这次,lex()
中的 getNonBlank()
调用“抛出 contents
中的 space 并重复与之前相同的过程。
我遇到错误的地方是最后一个词位。我被告知 globals()['contents'][0]
在被 getChar()
调用时超出了范围。我不认为这是一个很难发现的错误,但我已经尝试手动跟踪它,但似乎无法发现问题。任何帮助将不胜感激。
只是因为在成功读取输入字符串的最后一个3
后,digitfunc
函数又迭代了一次getchar
。但是在那一刻 content
已经用完并且是空的,所以 contents[0]
被传递到缓冲区的末尾,因此出现错误。
作为解决方法,如果您在表达式的最后一个字符后添加换行符或 space,您当前的代码不会出现问题。
原因是,当最后一个字符是 UNKNOWN
时,您会立即从 lex return 退出循环,因为 content
是空的,但是如果您正在处理数字或您循环调用 getchar
而不测试输入结束的符号。顺便说一下,如果你的输入字符串以右括号结尾,你的词法分析器 吃掉 它并忘记显示它找到它。
所以你至少应该:
getchar 中输入的测试结束:
def getchar():
if len(contents) == 0:
# print "END OF INPUT DETECTED"
globals()['charClass'] = UNKNOWN
globals()['nextChar'] = ''
return
...
如果还剩一个则显示最后一个标记:
...
while contents:
lex()
lex()
控制词素是否存在(输入结束时可能会发生奇怪的事情)
...
if charClass in lexDict.keys():
lexDict[charClass]()
if lexeme != '':
print('The next token is: '+ str(globals()['nextToken']) +
' The next lexeme is: >' + globals()['lexeme'] + '<')
但是您对全局变量的使用不好。在函数内使用全局变量的常见习惯是在使用前声明它:
a = 5
def setA(val):
global a
a = val # sets the global variable a
但是 Python 中的全局变量 代码味道 。您能做的最好的事情就是将您的解析器正确封装在 class 中。 对象优于全局
我必须将 Sebesda 的 Concpets of Programming Languages(第 4 章第 2 节)中的代码翻译成 python。这是我目前所拥有的:
# Character classes #
LETTER = 0
DIGIT = 1
UNKNOWN = 99
# Token Codes #
INT_LIT = 10
IDENT = 11
ASSIGN_OP = 20
ADD_OP= 21
SUB_OP = 22
MULT_OP = 23
DIV_OP = 24
LEFT_PAREN = 25
RIGHT_PAREN = 26
charClass = ''
lexeme = ''
lexLen = 0
token = ''
nextToken = ''
### lookup - function to lookup operators and parentheses ###
### and return the token ###
def lookup(ch):
def left_paren():
addChar()
globals()['nextToken'] = LEFT_PAREN
def right_paren():
addChar()
globals()['nextToken'] = RIGHT_PAREN
def add():
addChar()
globals()['nextToken'] = ADD_OP
def subtract():
addChar()
globals()['nextToken'] = SUB_OP
def multiply():
addChar()
globals()['nextToken'] = MULT_OP
def divide():
addChar()
globals()['nextToken'] = DIV_OP
options = {')': right_paren, '(': left_paren, '+': add,
'-': subtract, '*': multiply , '/': divide}
if ch in options.keys():
options[ch]()
else:
addChar()
### addchar- a function to add next char to lexeme ###
def addChar():
#lexeme = globals()['lexeme']
if(len(globals()['lexeme']) <=98):
globals()['lexeme'] += nextChar
else:
print("Error. Lexeme is too long")
### getChar- a function to get the next Character of input and determine its character class ###
def getChar():
globals()['nextChar'] = globals()['contents'][0]
if nextChar.isalpha():
globals()['charClass'] = LETTER
elif nextChar.isdigit():
globals()['charClass'] = DIGIT
else:
globals()['charClass'] = UNKNOWN
globals()['contents'] = globals()['contents'][1:]
## getNonBlank() - function to call getChar() until it returns a non whitespace character ##
def getNonBlank():
while nextChar.isspace():
getChar()
## lex- simple lexical analyzer for arithmetic functions ##
def lex():
globals()['lexLen'] = 0
getNonBlank()
def letterfunc():
globals()['lexeme'] = ''
addChar()
getChar()
while(globals()['charClass'] == LETTER or globals()['charClass'] == DIGIT):
addChar()
getChar()
globals()['nextToken'] = IDENT
def digitfunc():
globals()['lexeme'] = ''
addChar()
getChar()
while(globals()['charClass'] == DIGIT):
addChar()
getChar()
globals()['nextToken'] = INT_LIT
def unknownfunc():
globals()['lexeme'] = ''
lookup(nextChar)
getChar()
lexDict = {LETTER: letterfunc, DIGIT: digitfunc, UNKNOWN: unknownfunc}
if charClass in lexDict.keys():
lexDict[charClass]()
print('The next token is: '+ str(globals()['nextToken']) + ' The next lexeme is: ' + globals()['lexeme'])
with open('input.txt') as input:
contents = input.read()
getChar()
lex()
while contents:
lex()
我使用字符串 sum + 1 / 33
作为示例输入字符串。据我了解,在顶层第一次调用 getChar() 会将 characterClass 设置为 LETTER,并将 contents
设置为 um + 1 / 33
。
程序随后进入while循环并调用lex()
。 lex()
依次将单词 sum 累加到 lexeme
中。当 letterfunc
内的 while 循环遇到第一个 white-space 字符时,它中断,退出 lex()
由于contents
不为空,程序再次在顶层进行while循环。这次,lex()
中的 getNonBlank()
调用“抛出 contents
中的 space 并重复与之前相同的过程。
我遇到错误的地方是最后一个词位。我被告知 globals()['contents'][0]
在被 getChar()
调用时超出了范围。我不认为这是一个很难发现的错误,但我已经尝试手动跟踪它,但似乎无法发现问题。任何帮助将不胜感激。
只是因为在成功读取输入字符串的最后一个3
后,digitfunc
函数又迭代了一次getchar
。但是在那一刻 content
已经用完并且是空的,所以 contents[0]
被传递到缓冲区的末尾,因此出现错误。
作为解决方法,如果您在表达式的最后一个字符后添加换行符或 space,您当前的代码不会出现问题。
原因是,当最后一个字符是 UNKNOWN
时,您会立即从 lex return 退出循环,因为 content
是空的,但是如果您正在处理数字或您循环调用 getchar
而不测试输入结束的符号。顺便说一下,如果你的输入字符串以右括号结尾,你的词法分析器 吃掉 它并忘记显示它找到它。
所以你至少应该:
getchar 中输入的测试结束:
def getchar(): if len(contents) == 0: # print "END OF INPUT DETECTED" globals()['charClass'] = UNKNOWN globals()['nextChar'] = '' return ...
如果还剩一个则显示最后一个标记:
... while contents: lex() lex()
控制词素是否存在(输入结束时可能会发生奇怪的事情)
... if charClass in lexDict.keys(): lexDict[charClass]() if lexeme != '': print('The next token is: '+ str(globals()['nextToken']) + ' The next lexeme is: >' + globals()['lexeme'] + '<')
但是您对全局变量的使用不好。在函数内使用全局变量的常见习惯是在使用前声明它:
a = 5
def setA(val):
global a
a = val # sets the global variable a
但是 Python 中的全局变量 代码味道 。您能做的最好的事情就是将您的解析器正确封装在 class 中。 对象优于全局