add uann
This commit is contained in:
parent
fa20cc5af2
commit
f9f56b4e9b
6 changed files with 203 additions and 1 deletions
1
uann
1
uann
|
@ -1 +0,0 @@
|
||||||
Subproject commit 513173b22fa4776c54cae4bb6b78dcc8ec9acac9
|
|
BIN
uann.tar.gz
Normal file
BIN
uann.tar.gz
Normal file
Binary file not shown.
16
uann/Cargo.lock
generated
Normal file
16
uann/Cargo.lock
generated
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parsing"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
9
uann/Cargo.toml
Normal file
9
uann/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "parsing"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
unicode-segmentation = "1.6.0"
|
2
uann/README.md
Normal file
2
uann/README.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# uann
|
||||||
|
A compiler practice or a parser's practice
|
176
uann/src/main.rs
Normal file
176
uann/src/main.rs
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
/// (c) 2023 Tan Kian-ting <yoxem@kianting.info>
|
||||||
|
/// Under MIT License
|
||||||
|
/// 习包子 梁家河小学博士 清零宗 习炀帝 庆丰大帝
|
||||||
|
/// 独裁国贼 新疆集中营 光复香港时代革命 祈翠 南蒙古独立 香港独立
|
||||||
|
///
|
||||||
|
|
||||||
|
///
|
||||||
|
/// pairs of string for matching and parsing
|
||||||
|
///
|
||||||
|
/// - `matched` : the string being accumulatedly matched.
|
||||||
|
/// - `remained` : the string to be matched
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct Matchee {
|
||||||
|
matched : String,
|
||||||
|
remained : String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// macro convert a str as a string, equivalent to `x.to_string()`.
|
||||||
|
///
|
||||||
|
/// # Example:
|
||||||
|
/// ```
|
||||||
|
/// let a = "abc";
|
||||||
|
/// assert_eq!(string!(a), a.to_string());
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
macro_rules! string {
|
||||||
|
($name:expr) => {$name.to_string()}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// then!(a, b [, c...]*) i similar to
|
||||||
|
/// a ==> b ((==> c)...)*
|
||||||
|
macro_rules! then{
|
||||||
|
|
||||||
|
($item: expr, $closure1: expr, $($closure2: expr),+)=>{
|
||||||
|
then!(then_do($item, &$closure1), $($closure2),+);
|
||||||
|
};
|
||||||
|
($item: expr, $closure1: expr)=>{
|
||||||
|
then_do($item, &$closure1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// convert a string to
|
||||||
|
/// a vector of char
|
||||||
|
///
|
||||||
|
/// * s : input `str`'s reference
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let s = "Lí 好!";
|
||||||
|
/// let char_vec: Vec<char> = str_to_char_vec(s);
|
||||||
|
/// assert_eq!(char_vec, vec!['L','í',' ','好','!'])
|
||||||
|
/// ```
|
||||||
|
fn str_to_char_vec (s : &str) -> Vec<char>{
|
||||||
|
return s.chars().collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return a closure such that
|
||||||
|
/// if the 1st char of `Matchee.matched` matches `ch`,
|
||||||
|
/// then return the new `Some<Matchee>`. Otherwise, it returns `None`.
|
||||||
|
fn match_1_char(ch : char) -> Box<dyn Fn(Matchee) -> Option<Matchee>>{
|
||||||
|
return match_range(ch, ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return a closure such that
|
||||||
|
/// if the codepoint of the 1st char of `Matchee.matched` between
|
||||||
|
/// that of `lower_ch` (lower bound) and that of `upper_ch` (upper bound)
|
||||||
|
/// then return the new updated `Some<Matchee>`.
|
||||||
|
/// Otherwise, it returns `None`.
|
||||||
|
///
|
||||||
|
fn match_range(lower_ch : char, upper_ch: char) ->
|
||||||
|
Box<dyn Fn(Matchee) -> Option<Matchee>> {
|
||||||
|
Box::new(move | x : Matchee| -> Option<Matchee> {
|
||||||
|
let x_remained_str = x.remained.as_str();
|
||||||
|
let x_remained_char_vec = str_to_char_vec(x_remained_str);
|
||||||
|
|
||||||
|
if x_remained_char_vec.len() == 0{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x_remained_char_vec[0] as u32) >= (lower_ch as u32) &&
|
||||||
|
(x_remained_char_vec[0] as u32) <= (upper_ch as u32){
|
||||||
|
let remained_string = x_remained_char_vec[1..].iter()
|
||||||
|
.collect::<String>();
|
||||||
|
return Some(Matchee{
|
||||||
|
matched : x.matched + &x_remained_char_vec[0].to_string(),
|
||||||
|
remained : remained_string,
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// like the infix `==>` monad, i.e. `inputee ==> closure` in OCaml,
|
||||||
|
/// return a combinable closure.
|
||||||
|
/// - `inputee` : input string wrapped by Some() or None
|
||||||
|
/// - `closure` : the input to be processed
|
||||||
|
fn then_do(inputee : Option<Matchee>, closure : &dyn Fn(Matchee) -> Option<Matchee>)
|
||||||
|
-> Option<Matchee>{
|
||||||
|
return match inputee {
|
||||||
|
Some(inner) => closure(inner),
|
||||||
|
None => inputee,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// return a closure for what is do 0+ times
|
||||||
|
/// similar to `( closure )*`
|
||||||
|
fn zero_plus_times_do(closure : &dyn Fn(Matchee) -> Option<Matchee>) ->
|
||||||
|
Box<dyn Fn(Matchee) -> Option<Matchee> + '_>{
|
||||||
|
return Box::new(
|
||||||
|
move |inputee|{
|
||||||
|
let mut old_inputee = inputee.clone();
|
||||||
|
let mut new_inputee = closure(old_inputee.clone());
|
||||||
|
while let Some(new_inner) = new_inputee
|
||||||
|
{
|
||||||
|
old_inputee = new_inner.clone();
|
||||||
|
new_inputee = closure(new_inner);
|
||||||
|
}
|
||||||
|
return Some(old_inputee.clone());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return a combined closure. if `closure1` is not passed, then
|
||||||
|
/// use `closure2`, i.e. : `(closure1 || closure2)`
|
||||||
|
fn or_do(
|
||||||
|
closure1 : Box<dyn Fn(Matchee) -> Option<Matchee>>,
|
||||||
|
closure2 : Box<dyn Fn(Matchee) -> Option<Matchee>>) ->
|
||||||
|
Box<dyn Fn(Matchee) -> Option<Matchee>>{
|
||||||
|
Box::new(
|
||||||
|
move |inputee|{
|
||||||
|
let inputee_after_c1 = closure1(inputee.clone());
|
||||||
|
match inputee_after_c1 {
|
||||||
|
None => closure2(inputee.clone()),
|
||||||
|
_ => inputee_after_c1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let ex1 = Matchee{
|
||||||
|
matched : string!(""),
|
||||||
|
remained : string!("112")};
|
||||||
|
|
||||||
|
let d = match_range('0', '9');
|
||||||
|
println!("{:?}", then_do(then_do(then_do(Some(ex1.clone()), &d), &d), &d));
|
||||||
|
|
||||||
|
println!("{:?}", (ex1.clone()));
|
||||||
|
println!("{:?}", match_range('2', '9')(ex1.clone()));
|
||||||
|
println!("{:?}", match_1_char('0')(ex1.clone()));
|
||||||
|
println!("{:?}", match_1_char('1')(ex1.clone()));
|
||||||
|
|
||||||
|
let ex2 = Matchee{
|
||||||
|
matched : string!(""),
|
||||||
|
remained : string!("1234")};
|
||||||
|
println!("~~~{:?}",
|
||||||
|
then_do(then_do(Some(ex2.clone()), &or_do(match_1_char('1'),
|
||||||
|
match_1_char('0'))),&d));
|
||||||
|
|
||||||
|
println!("~~~{:?}",
|
||||||
|
then_do(Some(ex2.clone()),&zero_plus_times_do(&d)));
|
||||||
|
|
||||||
|
println!("~~~{:?}",
|
||||||
|
then!(Some(ex2.clone()),&zero_plus_times_do(&d)));
|
||||||
|
|
||||||
|
println!("~~~{:?}",
|
||||||
|
then!(Some(ex2.clone()), &d, &d, &d));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue