因为最近在做编译原理的课设而焦头烂额,需要设计词法分析器、语法分析器和语义分析器。因为网上可以参考的Python代码数量少之又少。也没有谁专门研究分词问题,因此我对此做了专门的研究。
注意:下面的例子及代码是根据C - -语法实现的,若你想对其他语言进行分词,则修改对应的正则式即可实现。
代码(此处以C - -源代码为例)
void main(){
// 注释
/*做词法分析器好难 */
/**分词**/
int aA,dd = 12;
int b= 14;
double c = a-- + b * 2.44 + 1.2j - 1.2E+12 + 12.34e-0.2;
int i;
bool b=a||d&&c;
l=1;
i-=1;
if(a<=b || b>c && a==c){
print("马老师发生甚么事了?");
print('a');
print("");
if(i==1){
print("1");
}
else{
print("2");
}
}
else if(a==2){
print("hello");
}
else{
if(1==2){
print(1);
}
else if(2==2){
print("积泥钛镁");
}
else{
print(2);
}
}
int i=0,j=0;
while(i<10){
print(1);
while(j<5){
print(2);
j++;
}
i++;
}
for(i=0;i<20;i++){
print("小黑子");
for(int j=0;j<12;j+=2){
print("ikun");
}
}
print(c);
bool lbw = false;
}
希望对下面的C++ 能实现一下操作:
1.过滤掉单行注释及多行注释。
2.准确提取出标识符、界符、关键字、常量、运算符。
其实这里面涉及到一个匹配的优先级,正确的处理方式应该是:
下面就是正式提取token分词了,下面的优先级很重要,如果顺序错了很容易导致提取不到数据。
细心的同学会发现,上面我提取到了错误的科学计数法表示,因为科学计数法的指数部分只能是整数,标识符亦如此。这样做的目的是便于后面识别用户的代码是否有语法错误。如果一开始就按照标准的形式进行提取,则很有可能会提取到错误的分词,导致后面难以识别。因此,推荐的做法是:把所有错误的可能都提取到,再去判断是否有语法错误,这样既简单又高效。
python实现的算法
import re
cpp_code = """
void main(){
// 注释
/*做词法分析器好难 */
/**分词**/
int _aA,dd = 12;
int 1b= 14;
double C2_c = a1-- + b * 2.44 + 1.2j - 1.2E+12 + 12.34e-0.2;
int i;
bool b=a||d&&c;
l=1;
i-=1;
if(a<=b || b>c && a==c){
print("马老师发生甚么事了?");
print('a');
print("");
if(i==1){
print("1");
}
else{
print("2");
}
}
else if(a==2){
print("hello");
}
else{
if(1==2){
print(1);
}
else if(2==2){
print("积泥钛镁");
}
else{
print(2);
}
}
int i=0,j=0;
while(i<10){
print(1);
while(j<5){
print(2);
j++;
}
i++;
}
for(i=0;i<20;i++){
print("小黑子");
for(int j=0;j<12;j+=2){
print("ikun");
}
}
print(c);
bool lbw = false;
}
"""
cpp_code_lines = cpp_code.split("\n")
cleaned_cpp_code_lines = []
for index, line in enumerate(cpp_code_lines):
cleaned_line = line.strip()
if cleaned_line and not re.match(r'^//', cleaned_line) and not re.match(r'^/\*', cleaned_line) and not re.match(
r'^\*/', cleaned_line):
cleaned_cpp_code_lines.append((index + 1, cleaned_line)) # 使用 index + 1 以保留原始索引
print(cleaned_cpp_code_lines)
def tokenize_code(lines):
tokenizer = []
cpp_keywords = r'\b(?:print|main|bool|false|true|void|while|if|else|int|double|for)\b'
string_literal = r'(?:"(?:[^"\\]|\\.)*"|\'(?:[^\'\\]|\\.)*\')'
delimiter = r'[:,;{}\[\]()]'
complex_number = r'\d+(?:\.\d+)?[ij]'
scientific_notation = r'\d+(?:\.\d+)?(?:[eE][+-]?\d+(?:\.\d+)?)'
floating_point = r'\d+\.\d+'
integer = r'\d+'
identifier = r'(?:\w{2,}|[a-zA-Z_])'
double_operators = r'\+\+|--|\+=|\-=|\*=|/=|\|\||&&|<=|>=|=='
single_operators = r'[+\-*/&|=!<>]'
combined_pattern = f'{cpp_keywords}|{string_literal}|{delimiter}|{complex_number}|{scientific_notation}|{identifier}|{floating_point}|{integer}|{double_operators}|{single_operators}'
pattern = re.compile(combined_pattern)
for line_number, line in lines:
tokens = pattern.findall(line)
tokenizer.append((line_number, tokens))
return tokenizer
tokenized_code = tokenize_code(cleaned_cpp_code_lines)
print("=="*60)
for i, value in tokenized_code:
print(f'[{i}]: ', ' '.join(value))
输出
[(2, 'func void main(){'), (7, 'int _aA,dd = 12;'), (8, 'int 1b= 14;'), (9, 'double C2_c = a1-- + b * 2.44 + 1.2j - 1.2E+12 + 12.34e-0.2;'), (10, 'int i;'), (11, 'bool b=a||d&&c;'), (12, 'l=1;'), (13, 'i-=1;'), (14, 'if(a<=b || b>c && a==c){'), (15, 'print("马老师发生甚么事了?");'), (16, "print('a');"), (17, 'print("");'), (18, 'if(i==1){'), (19, 'print("1");'), (20, '}'), (21, 'else{'), (22, 'print("2");'), (23, '}'), (24, '}'), (25, 'else if(a==2){'), (26, 'print("hello");'), (27, '}'), (28, 'else{'), (29, 'if(1==2){'), (30, 'print(1);'), (31, '}'), (32, 'else if(2==2){'), (33, 'print("积泥钛镁");'), (34, '}'), (35, 'else{'), (36, 'print(2);'), (37, '}'), (38, '}'), (39, 'int i=0,j=0;'), (40, 'while(i<10){'), (41, 'print(1);'), (42, 'while(j<5){'), (43, 'print(2);'), (44, 'j++;'), (45, '}'), (46, 'i++;'), (47, '}'), (48, 'for(i=0;i<20;i++){'), (49, 'print("小黑子");'), (50, 'for(int j=0;j<12;j+=2){'), (51, 'print("ikun");'), (52, '}'), (53, '}'), (54, 'print(c);'), (55, 'bool lbw = false;'), (56, '}')]
========================================================================================================================
[2]: func void main ( ) {
[7]: int _aA , dd = 12 ;
[8]: int 1b = 14 ;
[9]: double C2_c = a1 -- + b * 2.44 + 1.2j - 1.2E+12 + 12.34e-0.2 ;
[10]: int i ;
[11]: bool b = a || d && c ;
[12]: l = 1 ;
[13]: i -= 1 ;
[14]: if ( a <= b || b > c && a == c ) {
[15]: print ( "马老师发生甚么事了?" ) ;
[16]: print ( 'a' ) ;
[17]: print ( "" ) ;
[18]: if ( i == 1 ) {
[19]: print ( "1" ) ;
[20]: }
[21]: else {
[22]: print ( "2" ) ;
[23]: }
[24]: }
[25]: else if ( a == 2 ) {
[26]: print ( "hello" ) ;
[27]: }
[28]: else {
[29]: if ( 1 == 2 ) {
[30]: print ( 1 ) ;
[31]: }
[32]: else if ( 2 == 2 ) {
[33]: print ( "积泥钛镁" ) ;
[34]: }
[35]: else {
[36]: print ( 2 ) ;
[37]: }
[38]: }
[39]: int i = 0 , j = 0 ;
[40]: while ( i < 10 ) {
[41]: print ( 1 ) ;
[42]: while ( j < 5 ) {
[43]: print ( 2 ) ;
[44]: j ++ ;
[45]: }
[46]: i ++ ;
[47]: }
[48]: for ( i = 0 ; i < 20 ; i ++ ) {
[49]: print ( "小黑子" ) ;
[50]: for ( int j = 0 ; j < 12 ; j += 2 ) {
[51]: print ( "ikun" ) ;
[52]: }
[53]: }
[54]: print ( c ) ;
[55]: bool lbw = false ;
[56]: }
注意:上述的正则文法需要改成你自己需要的,这里仅仅为了示范,并不完整。
本文属于作者自主知识产权的原创文章,如须引用、转载,请注明来源:
版权声明:本文为CSDN博主「InetGeek」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34532102/article/details/129968800