parsing - 转义字符串的命名解析器

标签 parsing rust

我正在写一个 Nom RCS 的解析器. RCS 文件往往采用 ISO-8859-1 编码。其中一个语法产生式是针对字符串的。这是 @ 分隔符,文字 @ 符号被转义为 @@

@A String@ -> A String
@A @@ String@ -> A @ String

我有一个工作函数(见结尾)。 IResult 来自 Nom,您要么返回已解析的内容,加上其余未解析的输入,要么返回 Error/IncompleteCow 用于返回在原始输入切片上构建的引用(如果不需要转义),或者返回一个拥有的字符串(如果需要)。

是否有任何内置的 Nom 宏可以帮助进行此解析?

#[macro_use]
extern crate nom;
use std::str;
use std::borrow::Cow;
use nom::*;

/// Parse an RCS String
fn string<'a>(input: &'a[u8]) -> IResult<&'a[u8], Cow<'a, str>> {
    let len = input.len();
    if len < 1 {
        return IResult::Incomplete(Needed::Unknown);
    }
    if input[0] != b'@' {
        return IResult::Error(Err::Code(ErrorKind::Custom(0)));
    }
    // start of current chunk. Chunk is a piece of unescaped input
    let mut start = 1;
    // current char index in input
    let mut i = start;
    // FIXME only need to allocate if input turned out to need unescaping
    let mut s: String = String::new();
    // Was the input escaped?
    let mut escaped = false;
    while i < len {
        // Check for end delimiter
        if input[i] == b'@' {
            // if there's another @ then it is an escape sequence
            if i + 1 < len && input[i + 1] == b'@' {
                // escaped @
                i += 1; // want to include the first @ in the output
                s.push_str(str::from_utf8(&input[start .. i]).unwrap());
                start = i + 1;
                escaped = true;
            } else {
                // end of string
                let result = if escaped {
                    s.push_str(str::from_utf8(&input[start .. i]).unwrap());
                    Cow::Owned(s)
                } else {
                    Cow::Borrowed(str::from_utf8(&input[1 .. i]).unwrap())
                };
                return IResult::Done(&input[i + 1 ..], result);
            }
        }
        i += 1;
    }
    IResult::Incomplete(Needed::Unknown)
}

最佳答案

看起来使用 nom 库的方法是使用宏组合器。快速浏览 source code给一些nice examples解析器,包括解析带有转义字符的字符串。这是我想出的:

#[macro_use]
extern crate nom;

use nom::*;

named!(string< Vec<u8> >, delimited!(
    tag!("@"),
    fold_many0!(
        alt!(
            is_not!(b"@") |
            map!(
                complete!(tag!("@@")),
                |_| &b"@"[..]
            )
        ),
        Vec::new(),
        |mut acc: Vec<u8>, bytes: &[u8]| {
            acc.extend(bytes);
            acc
        }
    ),
    tag!("@")
));

#[test]
fn it_works() {
    assert_eq!(string(b"@string@"), IResult::Done(&b""[..], b"string".to_vec()));
    assert_eq!(string(b"@string with @@ escapes@"), IResult::Done(&b""[..], b"string with @ escapes".to_vec()));
    assert_eq!(string(b"@invalid string"), IResult::Incomplete(Needed::Size(16)));
}

如您所见,我只是使用 Vec::extend 将字节复制到一个向量中 - 您可以在这里更复杂,如果您返回一个 Cow 字节切片想要。

不幸的是,escaped! 宏在这种情况下似乎没有用,因为当终止符与转义字符(实际上是一个漂亮的常见情况)。

关于parsing - 转义字符串的命名解析器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40331573/

相关文章:

rust - 不了解此代码: Rust上的“移动/复制特征”错误

rust - Cargo 安装的模块存储在 Rust 项目中的什么位置?

c++ - 如何使用 spirit x3 将结果解析结果移动到结构中

javascript - 如何将 JS 函数的结果解析为 SVG 对象?

c++ - 使用 C++ 在 Z3 中使用 Z3_parse_smtlib2_string 获取 Unsat Core

rust - 调用返回字符串文字数组的函数,错误为 "cannot return value referencing local variable"

rust - 迭代并从 Options 向量中获取

c++ - 编译非常简单的 boost::spirit 语法时出错

android - Volley 内存不足错误

database - Rust返回leveldb数据库实例