【词法分析器】token分词技巧

【词法分析器】token分词技巧(C - -分词)

    • 背景
    • 目标
    • 分析
    • 算法代码


背景

因为最近在做编译原理的课设而焦头烂额,需要设计词法分析器、语法分析器和语义分析器。因为网上可以参考的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.准确提取出标识符、界符、关键字、常量、运算符。

分析

其实这里面涉及到一个匹配的优先级,正确的处理方式应该是:

  1. 首先去除单行注释及多行注释
  2. 去除每一行首尾的空白符
  3. 每一行先按照空白符进行分割,得到子串

下面就是正式提取token分词了,下面的优先级很重要,如果顺序错了很容易导致提取不到数据。

  1. 优先提取关键字(如int等)
  2. 再提取界符(如,:;{}[]()等)
  3. 再提取字符串常量(如’xxx’、“xxx”)
  4. 再提取复数常量(如6.28i、6.28j等)
  5. 再提取科学计数法常量(如12.34e-0.2、12.34E-0.2等)
  6. 再提取浮点数(如12.34等)
  7. 再提取标识符(如1y_、2Y_y等)
  8. 再提取整数(如12等)
  9. 再提取双目运算符(如>=、+=、$$等)
  10. 再提取单目运算符(如+、$等)

细心的同学会发现,上面我提取到了错误的科学计数法表示,因为科学计数法的指数部分只能是整数,标识符亦如此。这样做的目的是便于后面识别用户的代码是否有语法错误。如果一开始就按照标准的形式进行提取,则很有可能会提取到错误的分词,导致后面难以识别。因此,推荐的做法是:把所有错误的可能都提取到,再去判断是否有语法错误,这样既简单又高效。

算法代码

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

你可能感兴趣的:(python,算法,开发语言)