You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 10:57:02