elf_lojban/src/lex/pattern.rs

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()))];
}