我正在写一个 Nom RCS 的解析器. RCS 文件往往采用 ISO-8859-1 编码。其中一个语法产生式是针对字符串的。这是 @
分隔符,文字 @
符号被转义为 @@
。
@A String@ -> A String
@A @@ String@ -> A @ String
我有一个工作函数(见结尾)。 IResult
来自 Nom,您要么返回已解析的内容,加上其余未解析的输入,要么返回 Error
/Incomplete
。 Cow
用于返回在原始输入切片上构建的引用(如果不需要转义),或者返回一个拥有的字符串(如果需要)。
是否有任何内置的 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/