在 Python 中为 C++ 代码制作词法分析器时遇到问题
Facing an issue while making a lexical analyzer for C++ code in Python
我正在尝试从头开始为 C++ 代码制作一个非常简单的词法分析器 (Tokenizer),而不使用 PLY 或任何其他库。
到目前为止我完成的事情:
- 在字典中定义关键字、运算符。
- 定义了注释、文字等的正则表达式
我坚持的是:
问题 1:
现在我正在尝试创建一个函数 check_line(line)
,它将使用一行代码和 return 词典中的标记。例如:
check_line('int main()')
输出应该是:
Tokens = {'Keyword':'int', 'Keyword':'main', 'Opening parenthesis':'(','Closing Parenthesis':')'}
但我得到的输出是:
Tokens = {'Keyword':'main', 'Keyword':'main', 'Opening parenthesis':'(','Closing Parenthesis':')'}
因为main在这里覆盖了int。
有没有办法解决这样的问题?
问题 2:
当我在函数内部传递 check_line('int main()')
时,程序不匹配 main
因为这里我们有括号。我该如何解决这个问题。
我把我目前写的代码贴出来,请看一下,让我知道你的想法。
import re
# Keywords
keywords = ['const','float','int','struct','break',
'continue','else','for','switch','void',
'case','enum','sizeof','typedef','char',
'do','if','return','union','while','new',
'public','class','friend','main']
# Regular Expression for Identifiers
re_id = '^[_]?[a-z]*[A-Z]([a-z]*[A-Z]*[0-9]+)'
# Regular Expression for Literals
re_int_lit = '^[+-]?[0-9]+'
re_float_lit = '^[+-]?([0-9]*)\.[0-9]+'
re_string_lit = '^"[a-zA-Z0-9_ ]+"$'
# Regular expression of Comments
re_singleline_comment = '^//[a-zA-Z0-9 ]*'
re_multiline_comment = '^/\*(.*?)\*/'
operators = {'=':'Assignment','-':'Subtraction',
'+':'Addition','*':'Multiplication',
'/':'Division','++':'increment',
'--':'Decrement','||':'OR', '&&':'AND',
'<<':'Cout operator','>>':'Cin Operator',
';':'End of statement'}
io = {'cin':'User Input',
'cout':'User Output'}
brackets = {'[':'Open Square',']':'Close Square',
'{':'Open Curly','}':'Close Curly',
'(':'Open Small',')':'Close Small'}
# Function
def check_line(line):
tokens = {}
words = line.split(' ')
for word in words:
if word in operators.keys():
tokens['Operator ' + word] = word
if word in keywords:
tokens['Keywords'] = word
if re.match(re_singleline_comment,word):
break
return tokens
check_line('int main()')
输出:
{'Keywords': 'main'}
输出应该是:
Tokens = {'Keyword':'int', 'Keyword':'main', 'Opening parenthesis':'(','Closing Parenthesis':')'}
PS: 我还没有完成这些条件,只是想先解决这个问题。
字典对于这个函数来说是一个非常糟糕的数据结构选择,因为字典的本质是每个键都与一个对应的值相关联。
分词器应该 return 完全不同:有序的分词对象流。在一个简单的实现中,这可能是一个元组列表,但对于任何重要的应用程序,您很快就会发现:
Tokens不仅仅是一个句法类型和一个字符串。有很多重要的辅助信息,最值得注意的是令牌在输入流中的位置(用于错误消息)。
代币几乎都是按顺序消费的,一次生产多个并没有什么特别的优势。在 Python 中,生成器是生成标记流的更自然的方式。如果创建标记列表有用(例如,实现回溯解析器),那么逐行工作就没有意义了,因为换行符在 C++ 中通常是无关紧要的。
如评论中所述,C++ 标记并不总是由白色分隔 space,这在您的示例输入中很明显。 (main()
是不包含单个 space 字符的三个标记。)将程序文本拆分为标记流的最佳方法是在当前输入光标处重复匹配标记模式,return最长匹配项,并将输入光标移到匹配项上。
我正在尝试从头开始为 C++ 代码制作一个非常简单的词法分析器 (Tokenizer),而不使用 PLY 或任何其他库。
到目前为止我完成的事情:
- 在字典中定义关键字、运算符。
- 定义了注释、文字等的正则表达式
我坚持的是:
问题 1:
现在我正在尝试创建一个函数 check_line(line)
,它将使用一行代码和 return 词典中的标记。例如:
check_line('int main()')
输出应该是:
Tokens = {'Keyword':'int', 'Keyword':'main', 'Opening parenthesis':'(','Closing Parenthesis':')'}
但我得到的输出是:
Tokens = {'Keyword':'main', 'Keyword':'main', 'Opening parenthesis':'(','Closing Parenthesis':')'}
因为main在这里覆盖了int。
有没有办法解决这样的问题?
问题 2:
当我在函数内部传递 check_line('int main()')
时,程序不匹配 main
因为这里我们有括号。我该如何解决这个问题。
我把我目前写的代码贴出来,请看一下,让我知道你的想法。
import re
# Keywords
keywords = ['const','float','int','struct','break',
'continue','else','for','switch','void',
'case','enum','sizeof','typedef','char',
'do','if','return','union','while','new',
'public','class','friend','main']
# Regular Expression for Identifiers
re_id = '^[_]?[a-z]*[A-Z]([a-z]*[A-Z]*[0-9]+)'
# Regular Expression for Literals
re_int_lit = '^[+-]?[0-9]+'
re_float_lit = '^[+-]?([0-9]*)\.[0-9]+'
re_string_lit = '^"[a-zA-Z0-9_ ]+"$'
# Regular expression of Comments
re_singleline_comment = '^//[a-zA-Z0-9 ]*'
re_multiline_comment = '^/\*(.*?)\*/'
operators = {'=':'Assignment','-':'Subtraction',
'+':'Addition','*':'Multiplication',
'/':'Division','++':'increment',
'--':'Decrement','||':'OR', '&&':'AND',
'<<':'Cout operator','>>':'Cin Operator',
';':'End of statement'}
io = {'cin':'User Input',
'cout':'User Output'}
brackets = {'[':'Open Square',']':'Close Square',
'{':'Open Curly','}':'Close Curly',
'(':'Open Small',')':'Close Small'}
# Function
def check_line(line):
tokens = {}
words = line.split(' ')
for word in words:
if word in operators.keys():
tokens['Operator ' + word] = word
if word in keywords:
tokens['Keywords'] = word
if re.match(re_singleline_comment,word):
break
return tokens
check_line('int main()')
输出:
{'Keywords': 'main'}
输出应该是:
Tokens = {'Keyword':'int', 'Keyword':'main', 'Opening parenthesis':'(','Closing Parenthesis':')'}
PS: 我还没有完成这些条件,只是想先解决这个问题。
字典对于这个函数来说是一个非常糟糕的数据结构选择,因为字典的本质是每个键都与一个对应的值相关联。
分词器应该 return 完全不同:有序的分词对象流。在一个简单的实现中,这可能是一个元组列表,但对于任何重要的应用程序,您很快就会发现:
Tokens不仅仅是一个句法类型和一个字符串。有很多重要的辅助信息,最值得注意的是令牌在输入流中的位置(用于错误消息)。
代币几乎都是按顺序消费的,一次生产多个并没有什么特别的优势。在 Python 中,生成器是生成标记流的更自然的方式。如果创建标记列表有用(例如,实现回溯解析器),那么逐行工作就没有意义了,因为换行符在 C++ 中通常是无关紧要的。
如评论中所述,C++ 标记并不总是由白色分隔 space,这在您的示例输入中很明显。 (main()
是不包含单个 space 字符的三个标记。)将程序文本拆分为标记流的最佳方法是在当前输入光标处重复匹配标记模式,return最长匹配项,并将输入光标移到匹配项上。