157 lines
5.0 KiB
Rust
157 lines
5.0 KiB
Rust
use super::{cluster::Cluster, cluster::ClusterKind};
|
|
use Clust::*;
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub enum Clust {
|
|
C,
|
|
Cc,
|
|
V,
|
|
}
|
|
|
|
impl From<&'static [Clust]> for Pattern {
|
|
fn from(pat: &'static [Clust]) -> Self {
|
|
Self { pat }
|
|
}
|
|
}
|
|
|
|
pub struct Pattern {
|
|
pub pat: &'static [Clust],
|
|
}
|
|
|
|
impl Pattern {
|
|
pub const CV: Self = Self { pat: &[C, V] };
|
|
pub const V: Self = Self { pat: &[V] };
|
|
pub const CCV: Self = Self { pat: &[Cc, V] };
|
|
pub const CCVCV: Self = Self {
|
|
pat: &[Cc, V, C, V],
|
|
};
|
|
pub const CCVCCV: Self = Self {
|
|
pat: &[Cc, V, Cc, V],
|
|
};
|
|
pub const CVCCV: Self = Self {
|
|
pat: &[C, V, Cc, V],
|
|
};
|
|
pub const CVCCVCCV: Self = Self {
|
|
pat: &[C, V, Cc, V, Cc, V],
|
|
};
|
|
fn matches_inner(&self, strict: bool, clusters: &[Cluster]) -> bool {
|
|
let mut pat = self.pat.iter();
|
|
let mut clusters = clusters.iter();
|
|
let mut checking_vowel = false;
|
|
let mut previous_punct = false;
|
|
loop {
|
|
if checking_vowel {
|
|
if let Some(Cluster { s, kind }) = clusters.next() {
|
|
match kind {
|
|
ClusterKind::Consonant => {
|
|
if previous_punct {
|
|
break false;
|
|
}
|
|
checking_vowel = false;
|
|
if let Some(clust_kind) = pat.next() {
|
|
match clust_kind {
|
|
Clust::C => {
|
|
if s.as_str().chars().count() > 1 {
|
|
break false;
|
|
}
|
|
}
|
|
Clust::Cc => {
|
|
if s.as_str().chars().count() <= 1 {
|
|
break false;
|
|
}
|
|
}
|
|
Clust::V => break false,
|
|
}
|
|
} else {
|
|
break true;
|
|
}
|
|
}
|
|
ClusterKind::Whitespace | ClusterKind::Number | ClusterKind::Unknown => {
|
|
if pat.next().is_some() {
|
|
break false;
|
|
} else {
|
|
break true;
|
|
}
|
|
}
|
|
ClusterKind::Huhboo | ClusterKind::Glide => {
|
|
if previous_punct {
|
|
break false;
|
|
} else {
|
|
previous_punct = true
|
|
}
|
|
}
|
|
ClusterKind::Vowel => {
|
|
if previous_punct {
|
|
previous_punct = false;
|
|
} else if let Some(clust_kind) = pat.next() {
|
|
match clust_kind {
|
|
Clust::C | Clust::Cc => break false,
|
|
Clust::V => {}
|
|
}
|
|
} else {
|
|
break true;
|
|
}
|
|
}
|
|
}
|
|
} else if previous_punct || pat.next().is_some() {
|
|
break false;
|
|
} else {
|
|
break true;
|
|
}
|
|
} else if let Some(clust_kind) = pat.next() {
|
|
if let Some(Cluster { s, kind }) = clusters.next() {
|
|
match kind {
|
|
ClusterKind::Consonant => match clust_kind {
|
|
Clust::C => {
|
|
if s.as_str().chars().count() != 1 {
|
|
break false;
|
|
}
|
|
}
|
|
Clust::Cc => {
|
|
if strict {
|
|
if s.as_str().chars().count() <= 1 {
|
|
break false;
|
|
}
|
|
} else if s.as_str().chars().count() != 2 {
|
|
break false;
|
|
}
|
|
}
|
|
Clust::V => break false,
|
|
},
|
|
ClusterKind::Whitespace
|
|
| ClusterKind::Number
|
|
| ClusterKind::Huhboo
|
|
| ClusterKind::Glide
|
|
| ClusterKind::Unknown => break false,
|
|
ClusterKind::Vowel => match clust_kind {
|
|
Clust::C | Clust::Cc => break false,
|
|
Clust::V => {
|
|
if !strict {
|
|
checking_vowel = true;
|
|
}
|
|
}
|
|
},
|
|
}
|
|
} else {
|
|
break false;
|
|
}
|
|
} else {
|
|
break true;
|
|
}
|
|
}
|
|
}
|
|
pub(crate) fn matches(&self, clusters: &[Cluster]) -> bool {
|
|
self.matches_inner(false, clusters)
|
|
}
|
|
pub(crate) fn matches_strict(&self, clusters: &[Cluster]) -> bool {
|
|
self.matches_inner(true, clusters)
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn patterns_match() {
|
|
use super::clusterise;
|
|
assert![Pattern::CV.matches(&clusterise("do".into()))];
|
|
assert![Pattern::CV.matches(&clusterise("fa'i".into()))];
|
|
}
|