【问题标题】:How to parse the Redis RESP bulk string using nom?如何使用 nom 解析 Redis RESP 批量字符串?
【发布时间】:2019-02-20 16:00:08
【问题描述】:

我需要使用 nom 来解析 RESP 请求/回复。当我来批量字符串时,例如

"$6\r\nfoobar\r\n"

$-1\r\n

首先,我编写函数来从数据中提取 len

named!(signed_digits<&str, (Option<&str>, &str)>,
   pair!(
      opt!(alt!(tag!("+") | tag!("-"))),
      nom::digit
   )
);

named!(signed_integer<&str, i64>,
   map_res!(recognize!(signed_digits), FromStr::from_str)
);

named!(get_len_in_bulk_string<&str, i64>,
   do_parse!(
      tag!("$") >>
      len: signed_integer >>
      tag!("\r\n") >>
      (len)
   )
);

然后我根据len得到原始字符串:

named!(parse_bulk_string<&str, Record>,
  map_res!(gen_len_in_bulk_string, |n|{
    if n < 0 {
        Record::BulkString(None)
    } else {
        Record::BulkString(Some(take!(n)))
    }
  })
);

但是我得到一个编译错误:

Record::BulkString(Some(take!(n)))
                             ^ missing tokens in macro arguments

如何根据之前从文本中提取的 len 获取原始字符串?看来我不能在自己的闭包中使用take!

【问题讨论】:

  • 为什么不直接使用现有的 Redis 板条箱?
  • 只是为了学习如何使用 nom 编写解析器

标签: rust nom


【解决方案1】:

take! 这样的宏在第一个位置需要一个“隐式”参数:要解析的字符串。通常你看不到它,因为它是在嵌套在另一个 nom 宏中时隐式传递的。

但是,在这里,您是直接“调用”它,因此它需要明确地使用此参数。

相反,您可以这样做:

named!(get_bulk_string<&str, &str>,
   do_parse!(
      tag!("$") >>
      len: signed_integer >>
      string: take!(len) >>
      tag!("\r\n") >>
      (string)
   )
);

当然,这忽略了-1 的情况,你可以用一个开关来处理:

named!(get_bulk_string<&str, Option<&str>>,
   do_parse!(
      tag!("$") >>
      string: switch!(signed_integer,
        -1 => map!(take!(0), |_| None) |
        _ => map!(take!(42), |s| Some(s))
      ) >>
      tag!("\r\n") >>
      (string)
   )
);

【讨论】:

    猜你喜欢
    • 2020-09-10
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 2017-11-10
    • 2018-04-02
    • 1970-01-01
    • 2021-05-04
    相关资源
    最近更新 更多