1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/*!
regex-literal - regex literal enclosed by delimiters
===============================================================================
This crate provides a quick approach of creating regular expression [`Regex`] 
and sequence [`ReSequence`] from delimited literals at runtime. Its aim is to 
formalize regex literal in Rust computing.

## Background
In Rust Reference[^1], primitive types (boolean, numeric and textual) have own 
literal expressions that are evaluated as single tokens in source code at 
compile time. But it is not the case for regular expression (abbr. regex).

In many scripting languages that implement PCRE library[^2], a regex pattern 
is enclosed by a pair of delimiters, for example,`/pattern/im` in JavaScript. 
Regex engines in [Rust crate regex-automata](https://crates.io/crates/regex-automata), 
can only receive a general literal (&str) in building a one-pattern regex. 
In the interface of [Regex::new_many](https://docs.rs/regex-automata/latest/regex_automata/meta/struct.Regex.html#method.new_many), 
an array of many pattern strings is required, as there is no syntax 
for one string literal representing a compound regex.

## Features
The crate delivers literal formats for regex and regex sets with the 
following punctuations:

* `//` (a pair of forward slashes) as the default delimiters that enclose a 
pattern. 
 
* `[]` (a pair of square brackets) that hold a union of multiple patterns 
(abbr. as 'ReU').

* `<>` (a pair of angle brackets) that hold a sequence of regex patterns and/or
 pattern unions (abbr. as 'ReS') that iterates over consecutive matchings.

* `,` (comma) serves as seperator in between regex pattern literals, while any 
whitespace unicode character[^3] is skipped in parsing.

### Samples of regex literals
1. a simple pattern : `r#"/ab+c/"#`
2. a regex union literal: `r#"[/(?i)ab+c/,/(?u)\s{2}D+/]"#`
3. a regex sequence literal:  `r#"</(?i)ab+c/,/(?u)\s{2}D+/>"#`
4. another regex sequence literal: `r#"<[/(?i)ab+c/,/(?u)\s{2}D+/],/\s*\w+/>"#`

Note that [`crate::delimited::set_delimiter()`] allows choosing a customized 
delimiter from [`crate::delimited::DELIMITER_CHARS`](static@DELIMITER_CHARS).
In addition, [`crate::util`] module provides public functions of text 
convertion between undelimited and delimited patterns.

## Building Regex structs from regex-literal
The regular expression structs can be constructed via either 
[`crate::XRegex::from_str`]  or [`crate::XRegex::new`]. The former uses the 
default regex literal delimiter ("/" transcoded in [`crate::delimited::DELIMITER`]); 
the latter allows a customised delimiter. An easy alternative is to use macro 
`xregex!` [`crate::xregex`] when constructing XRegex with literals.

### Examples

```rust
use regex_literal::{XRegex,FromStr,Regex,Match,PatternID,Input,Anchored,xregex};

//example 0: create a XRegex structs from a one-pattern literal by xregex!()
let text = "abc123";
//construct XRegex
let mut xre = xregex!(r"/^[a-z]+\d{3}$/");
//get Regex from XRegex struct
let re = xre.get_regex().unwrap();
//check if the one pattern regex matches with the target text
assert!(re.is_match(text));

//example 1: create a XRegex struct from a one-pattern literal
let text0 = "abc123";
//create one-pattern literal
let re0 = r#"/^[a-z]+\d{3}$/"#;
//construct XRegex
let mut x = XRegex::from_str(re0).unwrap();
//get Regex from XRegex struct
let x_one_pattern = x.get_regex().unwrap();
//check if the one pattern regex matches with the target text
assert!(x_one_pattern.is_match(text0));
//find the first match if it exists
let m = x_one_pattern.find(text0);
assert_eq!(m,Some(Match::must(0,0..6)));

//example 2: create a XRegex struct from a one-pattern literal
let text1 = "ABBBC abc123";
let re1 = "!!!!(?i)ab+c!!!!";
//construct XRegex
let mut y = XRegex::new(re1,b"!!!!").unwrap();
//get Regex from XRegex struct
let y_one_pattern = y.get_regex().unwrap();
// check if this one pattern regex matches with the input
assert!(y_one_pattern.is_match(text1));
//find all non-overlapping leftmost matches
let matches:Vec<Match> = y_one_pattern.find_iter(text1).collect();
assert_eq!(matches,vec![Match::must(0,0..5),Match::must(0,6..9),]);

//example 3: create a XRegex struct from a multiple-pattern literal
let reu = r"[/(?i)ab+c/,/\w+/]";
let mut m1 = XRegex::from_str(reu).unwrap();
//get Regex from XRegex struct
let m_patterns = m1.get_regex().unwrap();
assert!(m_patterns.is_match(text1));
let m_matches:Vec<Match> = m_patterns.find_iter(text1).collect();
assert_eq!(m_matches,vec![Match::must(0,0..5),Match::must(0,6..9),Match::must(1,9..12)]); //non-overlapping leftmost matches

let expected = Some(Match::must(1,0..7));
let input = Input::new("23ABBBC abc&").anchored(Anchored::Pattern(PatternID::must(1)));//choose the specific pattern for input
let n_patterns = XRegex::from_str(reu).unwrap().get_regex().unwrap();
let mut caps = n_patterns.create_captures();
n_patterns.search_captures(&input,&mut caps);
assert_eq!(expected, caps.get_match());

//example 4: create a XRegex struct from a regex sequence literal
let xre2 = XRegex::from_str(r"</(?i)ab+c/,/^\w+?\d+$/>").unwrap();
let seq_slice = xre2.as_slice().unwrap();
let child_regex = &seq_slice[0];
assert!(child_regex.is_match("abc333"));

```

## Conversion of regex literals
1. [`crate::util::delimit`] and [`crate::util::undelimit`] provide regex 
literal conversion between undelimited and delimited forms.

### Examples

``` rust
# use regex_literal::util::{delimit,undelimit};
 
let delimiter = "/";
// a regex literal that includes delimiter(forward slash `/`)
let re1 = r"\d{2}/\d{2}/\d{4}";
let delimited1 = delimit(re1,delimiter);
let string1 = r"/\d{2}\/\d{2}\/\d{4}/";
assert_eq!(&delimited1[..],string1);

let undelimited = undelimit(&delimited1[..],delimiter).unwrap();
assert_eq!(&undelimited[..], re1);

```

2. [`crate::assembly::into_reu`] and [`crate::assembly::into_res`] annotate 
patterns with default delimiters into delimited literals of regular expression 
union and sequence accordingly. Note the transformations require feature "w".

### Examples

``` rust
# use regex_literal::assembly::into_reu;
let re1 = "(?i)ab+c";
let re2 = r"\w+";
let re_set = [re1,re2];
let reu = into_reu(&re_set);
assert_eq!(reu,r"[/(?i)ab+c/,/\w+/]".to_owned());

```


## Acknowledgements
[`regex-literal`] has adopted PCRE-style delimiters on top of regex engines in Rust crate regex-automata.

[^1]: [literal expressions](https://doc.rust-lang.org/reference/expressions/literal-expr.html)

[^2]: [PCRE flavor](https://pcre.org/original/doc/html/pcretest.html)

[^3]: [Unicode characters with property White_Space=yes](https://en.wikipedia.org/wiki/Whitespace_character#Unicode)

---



*/

//includes the changlog markdown file for documenting everything into one 

#![doc = include_str!("../CHANGELOG.md")]

#![allow(unused)]
extern crate alloc;
mod error;
pub mod util;

pub mod delimited;// testing, why not showing up documents?
pub use alloc::str::FromStr;//trait implemented in XRegex 
pub use delimited::*;

/// construct XRegex with the following arguments: $l - regex string literal, $d_bytes - the byte string literal of delimiter
#[macro_export]
macro_rules! xregex {
	($l:literal) => {
		$l.parse::<$crate::XRegex>().unwrap() //XRegex::from_str(literal).unwrap()
	};
	($l:literal,$d_bytes:literal) => {
		$crate::XRegex::new($l,$d_bytes).unwrap()	
	};
}

//https://doc.rust-lang.org/cargo/reference/features.html
///feature `w` for "wiring"
#[cfg(any(test, feature = "w"))]
pub mod assembly; 



//the #[cfg(test)] annotation on the tests module tells Rust to compile and run the test code only when you run cargo test, not when you run cargo build
#[cfg(test)] 
mod tests {
	use super::*;
	#[test]
	fn regex_from_single_pattern_literal()  {
		 let re_str =  "/(?i)ab+c/";
		 let mut re = XRegex::from_str(re_str).unwrap();
		 //test literal field
		 assert_eq!(re.literal.0 as u8, b'/');
		 assert_eq!(re.literal.1, "/(?i)ab+c/".to_owned());
		 let my_re = re.get_regex().unwrap();
		 assert!(my_re.is_match("ABBBC"));	
	}
	#[test]
	fn regex_from_pattern_union_str()  {
		let reunion_str =  "[/(?i)ab+c/,/(?u)\\s{2}D+/]";
		let mut re = XRegex::from_str(reunion_str).unwrap();
		let my_re_set = re.get_regex().unwrap();
		assert!(my_re_set.is_match("ABBBC"));
		assert!(my_re_set.is_match("  DD"));
	}

	
	#[test]
	fn resequence_from_literal() {
		//let seq_str =  r"</(?u)[\u4e00-\u9fa5]{4}/,/\d{3}/>";
		let seq_str = r"</\s+/,/(\p{script=Han}+)(\d{3})/>";
		let mut xregex = XRegex::from_str(seq_str).unwrap();
		let seq = xregex.as_slice().unwrap();
		assert_eq!(seq.len(),2);
		
		let regex_ws = &seq[0];
		let input = "  天下一家1234";
		let m1 = regex_ws.find(input);
		assert_eq!(m1,Some(Match::must(0,0..2)));
		let regex_words = &seq[1];
		//assert!(regex_words.is_match(input));
		let mut caps = regex_words.create_captures();
		regex_words.captures(input,&mut caps);
		let chinese_span = caps.get_group(1).unwrap();
		
		assert_eq!("天下一家",&input[chinese_span.start..chinese_span.end]);
		/*
		let mut captures = vec![];
		for (full,[chinese,digits]) in regex_words.captures_iter(&input).map(|c| c.extract()) {
			captures.push((chinese, digits.parse::<u16>()?));
		}
		//see Captures from https://docs.rs/regex-automata/latest/regex_automata/util/captures/struct.Captures.html#method.extract

		assert_eq!(captures,vec![("天下一家",1234)]);
		*/
		
	}

		#[test]	
		fn test_macro_xregex(){
			let mut x = xregex!("/(?i)ab+c/",b"/");
			let my_re = x.get_regex().unwrap();
			assert!(my_re.is_match("ABBBC"));	
		}
	
}


//this test succeed when run: cargo build --features "w" && cargo test ; https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options
//reference: https://doc.rust-lang.org/cargo/commands/cargo-build.html
#[cfg(test)] 
mod tests_on_w_feature{
	use super::*;
	use assembly::{assemble_reu,assemble_res,into_reu,into_res};
	#[test]
	fn test_assemble_reu(){
			let reu = r"[/(?i)ab+c/,/\w+/]";
			let m1 = XRegex::from_str(reu).unwrap();
			
			let re1 = "/(?i)ab+c/";
			let re2 = r"/\w+/";
			let arr_re = [re1,re2];
			let assembled = assemble_reu(&arr_re).unwrap();
			assert_eq!(reu.to_owned(),assembled);
			let m2 = XRegex::from_str(&assembled).unwrap();
			assert_eq!(m1,m2);		
	}

	#[test]
	fn test_assemble_res(){
			let res = r"</(?i)ab+c/,/\w+/>";
			let m1 = XRegex::from_str(res).unwrap();
			
			let re1 = "/(?i)ab+c/";
			let re2 = r"/\w+/";
			let arr_re = [re1,re2];
			let assembled = assemble_res(&arr_re).unwrap();
			assert_eq!(res.to_owned(),assembled);
			let m2 = XRegex::from_str(&assembled).unwrap();
			assert_eq!(m1,m2);		
	}

	#[test]
		fn test_into_reu(){
			let re1 = "(?i)ab+c";
			let re2 = r"\w+";
			let re_set = [re1,re2];
			let reu = into_reu(&re_set);
			assert_eq!(reu,r"[/(?i)ab+c/,/\w+/]".to_owned());
			
		}
	#[test]
		fn test_into_res(){
			let re1 = "(?i)ab+c";
			let re2 = r"\w+";
			let re_set = [re1,re2];
			let res = into_res(&re_set);
			assert_eq!(res,r"</(?i)ab+c/,/\w+/>".to_owned());
			
		}

}