Rust词法分析器(Lexer)字符串测试失败问题求助
问题现象
用Rust实现的词法分析器,非字符串类型Token的测试用例全部通过,但字符串相关测试全失败。比如针对输入"hello world"的测试用例:
fn test_03() { assert_eq!(lex("hello world"),vec![Token::Alpha(b'h'), Token::Alpha(b'e'), Token::Alpha(b'l'), Token::Alpha(b'l'), Token::Alpha(b'o'), Token::WhiteSpace, Token::Alpha(b'w'), Token::Alpha(b'o'), Token::Alpha(b'r'), Token::Alpha(b'l'), Token::Alpha(b'd'), Token::EOF]); }
执行测试时抛出错误:
thread 'test_03' panicked at 'assertion failed:
(left == right)
left:[Alpha(111), WhiteSpace, Alpha(100), EOF],
right:[Alpha(104), Alpha(101), Alpha(108), Alpha(108), Alpha(111), WhiteSpace, Alpha(119), Alpha(111), Alpha(114), Alpha(108), Alpha(100), EOF], tests/lex.rs:15:3
可见代码处理h后直接跳到空格,跳过了中间的e、l、l,只生成了Alpha(o),完全不符合预期。
问题根源
1. 字母处理逻辑的count重复递增
在lex函数的字母匹配分支(0x41..=0x5A | 0x61..=0x7A)中,通过while循环连续读取后续字母并递增count,但循环结束后,函数末尾又执行了一次count +=1,导致指针直接跳过中间所有字符。比如处理hello时:
- 初始
count=0(对应h) - while循环里
count递增到4(对应o) - 生成
Alpha(o)后,末尾的count +=1让count变成5(直接跳到空格) - 最终只生成了一个
Alpha(o),而非每个字母单独的Token
2. 字符串处理逻辑不符合Token定义
Token枚举里定义了Quote类型,但代码中处理双引号(0x22)时,把整个字符串内容转换成Keyword返回,没有生成QuoteToken,这和Token设计冲突,也是字符串测试失败的核心原因。
修复方案
1. 调整字母处理逻辑(匹配测试用例预期)
如果测试用例要求每个字母单独生成AlphaToken,可简化字母分支逻辑,去掉内部的while循环:
0x41..=0x5A | 0x61..=0x7A => { // 先检查是否是关键字,是则生成Keyword并移动指针 let mut keyword = vec![bytes[count]]; let mut temp_count = count; while temp_count + 1 < bytes.len() && (bytes[temp_count + 1] >= 0x41 && bytes[temp_count + 1] <= 0x5A || bytes[temp_count + 1] >= 0x61 && bytes[temp_count + 1] <= 0x7A) { keyword.push(bytes[temp_count + 1]); temp_count += 1; } match &keyword[..] { b"true" | b"false" | b"fn" | b"return" | b"let" => { count = temp_count; Token::Keyword(keyword) } // 非关键字生成单个Alpha Token _ => Token::Alpha(bytes[count]), } }
若不需要支持关键字(完全按照测试用例生成单个字母Token),可进一步简化为:
0x41..=0x5A | 0x61..=0x7A => Token::Alpha(bytes[count]),
2. 修复字符串处理逻辑
按照Token枚举定义,遇到双引号时生成QuoteToken,处理内部字符后再生成闭合QuoteToken:
0x22 => { tokens.push(Token::Quote); count += 1; // 处理引号内部字符 while count < bytes.len() && bytes[count] != 0x22 { let c = bytes[count]; let inner_token = match c { 0x41..=0x5A | 0x61..=0x7A => Token::Alpha(c), 0x30..=0x39 => Token::Digit(c), 0x20 | 0xA | 0x9 => Token::WhiteSpace, 0x28 => Token::LeftParen, 0x29 => Token::RightParen, 0x7B => Token::LeftCurly, 0x7D => Token::RightCurly, 0x3D => Token::Equal, 0x2B => Token::Plus, 0x2D => Token::Dash, 0x3B => Token::Semicolon, 0x2C => Token::Comma, _ => Token::Other, }; tokens.push(inner_token); count += 1; } // 添加闭合引号(如果存在) if count < bytes.len() { tokens.push(Token::Quote); count += 1; } skip_push = true; Token::Other // 占位,不会被加入tokens列表 }
同时在循环中添加skip_push标志,避免重复添加占位Token:
while count < bytes.len() { let mut skip_push = false; let token = match bytes[count] { // 字符串处理分支 0x22 => { /* 上述字符串处理逻辑 */ } // 其他分支不变 ... }; if !skip_push { tokens.push(token); count += 1; } }
完整修复后的lex函数示例
pub fn lex(input: &str) -> Vec<Token> { let bytes = input.as_bytes(); let mut tokens = vec![]; let mut count = 0; while count < bytes.len() { let mut skip_push = false; let token = match bytes[count] { // 处理双引号字符串 0x22 => { tokens.push(Token::Quote); count += 1; while count < bytes.len() && bytes[count] != 0x22 { let c = bytes[count]; let inner_token = match c { 0x41..=0x5A | 0x61..=0x7A => Token::Alpha(c), 0x30..=0x39 => Token::Digit(c), 0x20 | 0xA | 0x9 => Token::WhiteSpace, 0x28 => Token::LeftParen, 0x29 => Token::RightParen, 0x7B => Token::LeftCurly, 0x7D => Token::RightCurly, 0x3D => Token::Equal, 0x2B => Token::Plus, 0x2D => Token::Dash, 0x3B => Token::Semicolon, 0x2C => Token::Comma, _ => Token::Other, }; tokens.push(inner_token); count += 1; } if count < bytes.len() { tokens.push(Token::Quote); count += 1; } skip_push = true; Token::Other // 占位,不会被加入tokens } // 处理字母与关键字 0x41..=0x5A | 0x61..=0x7A => { let mut keyword = vec![bytes[count]]; let mut temp_count = count; while temp_count + 1 < bytes.len() && (bytes[temp_count + 1] >= 0x41 && bytes[temp_count + 1] <= 0x5A || bytes[temp_count + 1] >= 0x61 && bytes[temp_count + 1] <= 0x7A) { keyword.push(bytes[temp_count + 1]); temp_count += 1; } match &keyword[..] { b"true" | b"false" | b"fn" | b"return" | b"let" => { count = temp_count; Token::Keyword(keyword) } _ => Token::Alpha(bytes[count]), } } 0x30..=0x39 => Token::Digit(bytes[count]), 0x28 => Token::LeftParen, 0x29 => Token::RightParen, 0x7B => Token::LeftCurly, 0x7D => Token::RightCurly, 0x3D => Token::Equal, 0x2B => Token::Plus, 0x2D => Token::Dash, 0x20 | 0xA | 0x9 => Token::WhiteSpace, 0x3B => Token::Semicolon, 0x2C => Token::Comma, _ => Token::Other, }; if !skip_push { tokens.push(token); count += 1; } } tokens.push(Token::EOF); tokens }
验证修复
修改后重新运行test_03,测试用例应能正常通过。同时字符串相关测试(比如输入"test string")会生成Quote+Alpha序列+Quote的Token,符合预期。
内容的提问来源于stack exchange,提问作者adviNess

