14 个版本 (3 个稳定版本)

1.1.0 2024年7月6日
0.9.5 2024年6月4日

#35 in 解析工具

Download history 514/week @ 2024-05-22 869/week @ 2024-05-29 370/week @ 2024-06-05 57/week @ 2024-06-12 25/week @ 2024-06-19 118/week @ 2024-07-03 17/week @ 2024-07-10 11/week @ 2024-07-24 97/week @ 2024-07-31

每月108次 下载
用于 mini-c-parser

MIT 许可

240KB
6K SLoC

RustyParser

用 Rust 编写的通用编译时解析器生成器和模式匹配库

RustyParser 提供了一组基本解析器、组合器和解析生成函数。

该库旨在与通用迭代器一起工作,但某些功能仅限于特定迭代器。

示例

示例代码

// import rusty_parser
use rusty_parser as rp;

// useful trait member functions
use rp::IntoParser;

#[test]
fn example1() {
    // define pattern
    // digit: [0-9]
    // this will match one digit, and returns (char,), the character it parsed
    let digit_parser = rp::range('0'..='9');

    // define pattern
    // num: digit+
    // this will match one or more digits, and returns (Vec<char>,), the character it parsed
    let num_parser = digit_parser.repeat(1..);

    // map the output
    // Vec<char>  -->  i32
    let num_parser = num_parser.map(|digits:Vec<char>| -> i32 {
        let mut num = 0;
        for ch in digits {
            num = num * 10 + (ch as i32 - '0' as i32);
        }
        num
    });

    // parse input iterator with given pattern, and return the result
    let res = rp::parse(&num_parser, "123456hello_world".chars());

    // res contains the result of parsing
    assert_eq!(res.output.unwrap(), (123456,));

    // res.it: iterator after parsing
    // here, '123456' is parsed, so the rest is "hello_world"
    assert_eq!(res.it.collect::<String>(), "hello_world");
}

结构体

RustyParser 提供了一组基本解析器、组合器和解析生成函数。这些生成的解析器用于解析输入字符串,并返回提取的数据。

fn parse(pattern:&Pattern, it:It) -> ParseResult<(Parsed Output of Pattern), It>;
fn match_pattern(pattern:&Pattern, it:It) -> ParseResult<(), It>;
  • parse(...) 接收一个模式对象和输入字符串的迭代器,然后返回 ParseResult<Self::Output, It>

  • match_pattern(...) 用于只想检查模式是否匹配,而不提取数据的情况。对于某些解析器,如 repeat,由于它会在内部调用 parse(...) 并调用 Vec::push,因此调用 parse(...) 获取输出非常昂贵。

ParseResult 是一个表示解析结果的 struct。

pub struct ParseResult<Output, It>
where
    Output: Tuple,
    It: Iterator + Clone,
{
    // the output; extracted data
    // 'None' means parsing failed
    pub output: Option<Output>,

    // iterator after parsing
    // if parsing failed, this will be the same as the input iterator
    pub it: It,
}

注意

  • 由于 parse(...) 内部会克隆迭代器,因此迭代器必须是廉价可克隆的。
  • Output 必须是 Tuple,包括 ()。如果需要返回单个值,请使用 (Value,)

解析器概述

基本(叶子)解析器

解析器 描述 输出
one, one_by 匹配一个字符 (迭代器::,)
范围 匹配范围内的一个字符 (迭代器::,)
str, str_by, slice, slice_by 匹配多个字符 ()
string, string_by, vec, vec_by 匹配多个字符 ()
检查 使用闭包检查一个字符 (T,)
任何 匹配任何字符 (迭代器::,)
DictBTree, DictHashMap 字典树 T
DynBoxChars, DynBoxSlice, DynBoxSliceCopied 可以接受任何具有相同 Output 的解析器的动态解析器 T

组合器

组合器 描述 输出
seq 解析器的序列 ( *<解析器 A 的输出>, *<解析器 B 的输出> ... )(元组连接)
或组合器 所有解析器的 Output
map 映射解析器的输出 (T,)
repeat 重复解析器多次 (Vec<输出自身>,)
optional 无论模式是否匹配都成功 ( Option<输出自身>, )
optional_or, or_else 无论模式是否匹配都成功 Output of Self
not 对于模式1成功且模式2失败进行匹配 Output of Self
reduce_left, reduce_right 减少解析器的输出 Output of Self
reduce_with, reduce_right_with 使用初始值减少解析器的输出 Init

其他

解析器 描述 输出
constant 始终成功,并返回常量值 ()
end 如果达到输入的末尾则成功 ()
fail 始终失败 ()
void 忽略解析器的输出 ()
output 将解析器的输出更改为 (output,) (T,)
string, vec 将匹配的范围捕获到 StringVec<T> (String,)(Vec<Iterator::Item>,)
not_consume 检查模式是否匹配,而不消耗输入 Output of Self

或参考 docs.rs

解析器的详细说明和示例

基本解析器

one, one_by:如果等于 c,则消耗一个字符。

let parser = one( c: CharType )

let a_parser = one('a')
let a_parser = 'a'.into_parser()

let a_parser = one_by('a', |value:char, ch:&char| value.to_ascii_lowercase() == *ch );

输出(Iterator::Item,)

range:如果它在范围 r 内,则消耗一个字符。

let parser = range( r: impl std::ops::RangeBounds )

let digit_parser = range( '0'..='9' )
let digit_parser = ('0'..='9').into_parser()

输出(Iterator::Item,)

strstr_bysliceslice_by:如果等于 s,则消耗多个字符。

出于借用安全性的考虑,str 或 slice 的生命周期必须是 'static。

要与其他生命周期一起使用,应使用 string()vec()。这些函数将复制 StringVec 中的项。

// must be 'static
let hello_parser = str("hello");
let hello_parser = "hello".into_parser();
let hello_parser = str_by("hello", |value:char, ch:char| value.to_ascii_lowercase() == ch );

let hello_parser = slice(&[104, 101, 108, 108, 111]);
let hello_parser = (&[104, 101, 108, 108, 111]).into_parser();
let hello_parser = slice_by(&[104, 101, 108, 108, 111], |value:i32, ch:&i32| value == *ch );

输出()

stringstring_byvecvec_by:如果等于 s,则消耗多个字符。

这将把所有字符复制到 StringVec 中,因此生命周期属于解析器本身。

let hello_parser = string("hello".to_string());
let hello_parser = "hello".to_string().into_parser();
let hello_parser = string_by("hello".to_string(), |value:char, ch:char| value.to_ascii_lowercase() == ch );

let hello_parser = vec(vec![104, 101, 108, 108, 111]);
let hello_parser = (vec![104, 101, 108, 108, 111]).into_parser();
let hello_parser = vec_by(vec![104, 101, 108, 108, 111], |value:i32, ch:&i32| value == *ch );

输出()

check:使用闭包检查单个字符

闭包必须是以下类型之一: Fn(Iterator::Item) -> Option<NewOutput>Fn(Iterator::Item) -> bool

let parser = check( |ch:char| if ch.is_alphabetic() { Some(ch) }else{ None } ); // returns Option<char> -> `(char,)` as output
let parser = check( |ch:char| ch.is_alphabetic() ); // returns bool -> `()` as output

如果闭包返回 Option<NewOutput>,则输出将为 (NewOutput,)。如果闭包返回 bool,则输出将为 )

any:匹配任何字符。

let parser = any();

输出(Iterator::Item,)

字典:从字符串列表构建 Trie

// let mut parser = rp::DictBTree::new();
let mut parser = rp::DictHashMap::new();

parser.insert("hello".chars(), (1,));
parser.insert("hello_world".chars(), (2,));
parser.insert("world".chars(), (3,));

// this will match as long as possible
let res = rp::parse(&parser, "hello_world_abcdefg".chars());
assert_eq!(res.output.unwrap(), (2,));
// 'hello_world' is parsed, so the rest is "_abcdefg"
assert_eq!(res.it.collect::<String>(), "_abcdefg");

// match 'hello' only
let res = rp::parse(&parser, "hello_wo".chars());
assert_eq!(res.output.unwrap(), (1,));

Output:您插入的泛型类型

这将匹配尽可能多的字符,而不管插入顺序如何。

字典有两种类型:用于 Trie 实现的 DictBTreeDictHashMap。它们各有优缺点(内存使用和搜索时间复杂度),因此您可以选择其中之一。

组合器

seq:解析器序列

// 'a', and then 'b'
let ab_parser = rp::seq!('a', 'b', 'c'); // IntoParser for char

let res = rp::parse(&ab_parser, "abcd".chars());
assert_eq!(res.output.unwrap(), ('a', 'b', 'c')); // Output is concatenated
assert_eq!(res.it.collect::<String>(), "d");

输出: ( A0, A1, ..., B0, B1, ..., C0, C1, ... ) 其中 (A0, A1, ...) 是第一个解析器的输出,而 (B0, B1, ...)(C0, C1, ...) 是后续解析器的输出。

: 或组合器

// 'a' or 'b'
let ab_parser = rp::or!('a', 'b'); // IntoParser for char

// 'a' is matched
let res = rp::parse(&ab_parser, "abcd".chars());
assert_eq!(res.output.unwrap(), ('a',)); // Output of 'a'
assert_eq!(res.it.clone().collect::<String>(), "bcd");

// continue parsing from the rest
// 'a' is not matched, but 'b' is matched
let res = rp::parse(&ab_parser, res.it);
assert_eq!(res.output.unwrap(), ('b',));
assert_eq!(res.it.clone().collect::<String>(), "cd");

// continue parsing from the rest
// 'a' is not matched, 'b' is not matched; failed
let res = rp::parse(&ab_parser, res.it);
assert_eq!(res.output, None);
assert_eq!(res.it.clone().collect::<String>(), "cd");

输出: 所有解析器的 输出。注意,所有解析器的输出必须是同一类型。

map: 将解析器的输出映射

解析器的输出(元组)将被解包并传递给闭包。闭包返回的值将是新的输出。

// map the output
// <Output of 'a'> -> i32
let int_parser = 'a'.map(|ch| -> i32 { ch as i32 - 'a' as i32 }); // IntoParser for char

let res = rp::parse(&int_parser, "abcd".chars());
assert_eq!(res.output.unwrap(), (0,));
assert_eq!(res.it.collect::<String>(), "bcd");

输出: (T,) 其中 T 是闭包的返回类型。闭包返回的值 v 将被包裹成 (v,)

repeat: 重复解析器多次

// repeat 'a' 3 to 5 times
let multiple_a_parser = 'a'.repeat(3..=5); // IntoParser for char
let res = rp::parse(&multiple_a_parser, "aaaabcd".chars());

// four 'a' is parsed
assert_eq!(res.output.unwrap(), (vec!['a', 'a', 'a', 'a',],));
assert_eq!(res.it.collect::<String>(), "bcd");

输出:

  • 如果重复解析器的 输出(),则 输出()
  • 如果重复解析器的 输出(T,),则 输出Vec<T>
  • 否则,Vec<Self 的输出Output >

optionaloptional_oror_else:无论模式是否匹配,都成功

let a_optional_parser = 'a'.optional(); // (Option<char>,)

let res = rp::parse(&a_optional_parser, "abcd".chars()); // success
assert_eq!(res.output.unwrap(), (Some('a'),));

let res = rp::parse(&a_optional_parser, "bcd".chars()); // success, but 'a' is not matched
assert_eq!(res.output.unwrap(), (None,));

// if 'a' failed, return 'x'
let a_optional_or = 'a'.optional_or(('x',)); // (char,)

let res = rp::parse(&a_optional_or, "bcd".chars());
assert_eq!(res.output.unwrap(), ('x',));

// if 'a' failed, evaluate the closure
let a_or_else = 'a'.or_else( || 'x' ); // (char,)
assert_eq!(res.output.unwrap(), ('x',));

optional输出

  • 如果原始解析器的 输出(T0,)(Option<T0>,)
  • 否则,( Option<Self 的输出Output>, )

optional_oror_else输出

  • <Self 的输出Self>.

注意

  • 传递给 optional_oror_else 的值类型必须与 SelfOutput 相匹配
  • 对于单值输出(其输出为 (T,) ),传递 T(T,) 都是允许的。

not:匹配 Pattern1 成功,Pattern2 失败

// all digit but not 4
let digit_parser_except_4 = ('0'..='9').not('4');

let res = rp::parse(&digit_parser_except_4, "3".chars());
assert_eq!(res.output.unwrap(), ('3',));

let res = rp::parse(&digit_parser_except_4, "4".chars());
assert_eq!(res.output, None);

Output:类型 SelfOutput

reduce_left:减少解析器的输出

给定输入字符串 self rhs rhs rhs rhs ... 和减法器 f,输出将计算为 f( f( f(self,rhs), rhs ), rhs ), ...

注意

  • 减法器的签名必须是 Fn(A0, A1, A2, ..., B0, B1, B2, ..., ) -> ( A0, A1, A2 ... )。其中 (A0, A1, A2, ...) 是第一个解析器的输出,而 (B0, B1, B2, ...) 是后续解析器的输出。

  • 对于单值输出(其输出为 (T,)),返回 T(T,) 都是允许的。

let digit_parser = ('0'..='9').into_parser().map(|val: char| -> i32 { val as i32 - '0' as i32 });
let reduced_left = digit_parser.reduce_left(digit_parser, |lhs, rhs| lhs * 10 + rhs);
let res = rp::parse( &reduced_left, "123456abcd".chars() );
assert_eq!(res.output.unwrap(), (123456,));
assert_eq!(res.it.collect::<String>(), "abcd");

Output:类型 SelfOutput

reduce_right:减少解析器的输出

给定输入字符串 lhs lhs lhs lhs ... self 和减法器 f,输出将计算为 f(lhs, f(lhs, f(lhs, f( ... f(lhs,self)))

注意

  • 还原器的签名必须是 Fn(A0, A1, A2, ..., B0, B1, B2, ...) -> ( B0, B1, B2 ... )。其中 (A0, A1, A2, ...) 是第一个解析器的输出,而 (B0, B1, B2, ...) 是后续解析器的输出。

  • 对于单值输出(其输出为 (T,)),返回 T(T,) 都是允许的。

let digit_parser =
    ('0'..='9').into_parser().map(|val: char| -> i32 { val as i32 - '0' as i32 });
let alphabet_parser =
    ('a'..='z').into_parser().map(|val: char| -> i32 { val as i32 - 'a' as i32 });
let reduced_right =
    alphabet_parser.reduce_right(digit_parser, |lhs: i32, rhs: i32| -> i32 { rhs * 10 + lhs });

let res = rp::parse(&reduced_right, "123456dcba".chars());
assert_eq!(res.output.unwrap(), (3654321,));
assert_eq!(res.it.collect::<String>(), "cba");

Output:类型 SelfOutput

reduce_with:使用初始值对解析器的输出进行还原

给定输入字符串 self self self ... 和还原器 f,输出将计算为 f( f( f(init,self), self), self), ...

还原器的签名必须是 Fn(Init, A0, A1, A2, ...) -> Init。其中 (A0, A1, A2, ...)Self 的输出。

Output: Init

let digit_parser =
    ('0'..='9').into_parser().map(|val: char| -> i32 { val as i32 - '0' as i32 });
let number_parser =
    digit_parser.reduce_with(0, |acc, rhs| acc * 10 + rhs);

let res = rp::parse(&number_parser, "123456abc".chars());
assert_eq!(res.output.unwrap(), (123456,));
assert_eq!(res.it.collect::<String>(), "abc");

reduce_right_with: 使用初始值对解析器的输出进行还原

给定输入字符串 self self self ... 和还原器 f,输出将计算为 f(self, f(self, f(self, f( ... f(self,init)))

归约器的签名必须是 Fn(A0, A1, A2, ..., Init) -> Init。其中 (A0, A1, A2, ...)Self 的输出。

Output: Init

示例

let digit_parser =
    ('0'..='9').into_parser().map(|val: char| -> i32 { val as i32 - '0' as i32 });
let number_rev_parser =
    digit_parser.reduce_right_with(0, |lhs, acc| acc * 10 + lhs);

let res = rp::parse(&number_rev_parser, "123456abc".chars());
assert_eq!(res.output.unwrap(), (654321,));
assert_eq!(res.it.collect::<String>(), "abc");

其他

简单但有用的解析器

constant:这个解析器始终成功,并返回常量值

let parser = rp::constant( (1, 2, 3) );

Output:您提供的元组值

end:如果到达输入的末尾则成功

let end_parser = rp::end();

输出()

fail:这个解析器始终失败

let parser = rp::fail();

输出()

void:忽略解析器的输出

强制输出为 ()。它内部调用 match_pattern(...) 而不是 parse(...)。这在您只想检查模式是否匹配时非常有用。更多信息请参阅上面的 match_pattern(...)

let expensive_parser = 'a'.map(|_| -> i32 {
    // some expensive operations for data extracting...
    panic!("This should not be called");
});
let expensive_parser = expensive_parser.void();

// ignore the output of parser
// this internally calls 'match_pattern(...)' instead of 'parse(...)'
let res = rp::parse(&expensive_parser, "abcd".chars());
assert_eq!(res.output.unwrap(), ());
assert_eq!(res.it.collect::<String>(), "bcd");

输出()

output:将解析器的输出更改为 (output,)

let digit_parser = ('0'..='9').output(2024);

let res = rp::parse(&digit_parser, "123456hello_world".chars());
assert_eq!(res.output.unwrap(), (2024,));
assert_eq!(res.it.collect::<String>(), "23456hello_world");

Output(T,) 其中 T 是您提供的值的类型。

stringvec:将匹配的范围捕获到 StringVec<T>

注意

string 只能用于 std::str::Chars,而 vec 只能用于 ExactSizeIterator

let digits_parser = ('0'..='9').repeat(0..).string();

let res = rp::parse(&digits_parser, "123456hello_world".chars());
assert_eq!(res.output.unwrap(), ("123456".to_string(),));
assert_eq!(res.it.collect::<String>(), "hello_world");

Output(String,)(Vec<Iterator::Item>,)

not_consume:检查模式是否匹配,而不消耗输入

let digit_parser = ('0'..='9').not_consume();

let res = rp::parse(&digit_parser, "12345".chars());
assert_eq!(res.output.unwrap(), ('1',));
assert_eq!(res.it.collect::<String>(), "12345"); // iterator is not consumed

Output:类型 SelfOutput

对于复杂、递归的模式

默认情况下,所有 'parser-generating' 函数都会消耗输入解析器并返回一个新的实例。这些过程在编译时完全创建新的泛型解析器对象。

然而,在某些情况下,您可能想定义一个递归解析器。这涉及到 'parser-reference' 或 '类似虚拟类' 的结构。

幸运的是,Rust std 为这些情况提供了包装器。 RcRefCellBox 是最常见的。

对于 RcRefCell,您可以用它们包裹任何解析器。它们将被视为 Parser 对象。

// making shared, interior-mutable parser
let hello_parser = "hello".into_parser();
let hello_parser = std::cell::RefCell::new(hello_parser);
let hello_parser = std::rc::Rc::new(hello_parser);

对于 Box,你可以使用 DynBox* 来包装任何解析器。使用 DynBox*,你可以分配具有相同 Output 类型的 任何解析器

let hello_parser = "hello".into_parser();

let mut dynamic_parser: DynBoxChars<(char,)> = Default::new(); // Default implemented
dynamic_parser.parse( "hello".chars() ); // this will panic, since the parser is not assigned yet

// set dynamic_parser to hello_parser
dynamic_parser.assign( "hello" );
let res = dynamic_parser.parse( "hello".chars() ); // success

// set dynamic_parser to digit_parser
dynamic_parser.assign( '0'..='9' );
let res = dynamic_parser.parse( "01234".chars() ); // success

Default 特性是通过始终引发解析器实现的。你必须稍后分配它。

目前,有三种类型的 DynBox*

  • DynBoxChars<Output>:用于 std::str::Chars
  • DynBoxSlice<Output,T>:用于 std::iter::Cloned<std::slice::Iter<T>>
  • DynBoxSliceCopied<Output,T>:用于 std::iter::Copied<std::slice::Iter<T>> 一旦通过 DynBox* 包装了解析器,你只能使用 parse(...) 中的相应迭代器。

你可以参考这里来实现其他迭代器类型。

无运行时依赖