14 个版本 (3 个稳定版本)
1.1.0 | 2024年7月6日 |
---|---|
0.9.5 | 2024年6月4日 |
#35 in 解析工具
每月108次 下载
用于 mini-c-parser
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 |
将匹配的范围捕获到 String 或 Vec<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,)
str
、str_by
、slice
、slice_by
:如果等于 s
,则消耗多个字符。
出于借用安全性的考虑,str 或 slice 的生命周期必须是 'static。
要与其他生命周期一起使用,应使用 string()
或 vec()
。这些函数将复制 String
、Vec
中的项。
// 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 );
输出
:()
string
、string_by
、vec
、vec_by
:如果等于 s
,则消耗多个字符。
这将把所有字符复制到 String
或 Vec
中,因此生命周期属于解析器本身。
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 实现的 DictBTree
和 DictHashMap
。它们各有优缺点(内存使用和搜索时间复杂度),因此您可以选择其中之一。
组合器
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 >
optional,optional_or,or_else
:无论模式是否匹配,都成功
optional_or,or_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_or,
or_else
的 输出
- <Self 的输出Self>.
注意
- 传递给
optional_or
和or_else
的值类型必须与Self
的Output
相匹配 - 对于单值输出(其输出为
(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
:类型 Self
的 Output
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
:类型 Self
的 Output
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
:类型 Self
的 Output
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
是您提供的值的类型。
string
,vec
:将匹配的范围捕获到 String
或 Vec<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
:类型 Self
的 Output
对于复杂、递归的模式
默认情况下,所有 'parser-generating' 函数都会消耗输入解析器并返回一个新的实例。这些过程在编译时完全创建新的泛型解析器对象。
然而,在某些情况下,您可能想定义一个递归解析器。这涉及到 'parser-reference' 或 '类似虚拟类' 的结构。
幸运的是,Rust std 为这些情况提供了包装器。 Rc
,RefCell
,Box
是最常见的。
对于 Rc
和 RefCell
,您可以用它们包裹任何解析器。它们将被视为 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(...)
中的相应迭代器。
你可以参考这里来实现其他迭代器类型。