use std::{ fmt, sync::Arc };
use crate :: {
error :: RuleSyntaxError,
rule :: { BinMod, SpecMod, ModKind, Rule },
word :: { Diacritic, FeatKind, FeatureCategory, NodeKind, Segment, SupraKind, Tone },
CARDINALS_MAP, DIACRITS
};
use super :: { AlphaMod, Modifiers, Mods, Position, Token, TokenKind };
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnvItem {
pub(crate) envs: Vec<Env>,
pub(crate) position: Position,
}
impl EnvItem {
pub(crate) fn reverse(&mut self) {
for env in &mut self.envs { env.reverse(); }
}
}
impl fmt::Display for EnvItem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for env in &self.envs {
let xb = env.before.iter()
.fold(String::new(), |acc, i| acc + &i.to_string() + ", ");
let xa = env.after.iter()
.fold(String::new(), |acc, i| acc + &i.to_string() + ", ");
let asdf = if let Some(und) = &env.center {
format!("{und:?}")
} else {
String::new()
};
if xb.is_empty() && xa.is_empty() {
write!(f, "[{xb}] _{asdf}_ [{xa}]")?
} else if xb.is_empty() {
write!(f, "[{xb}] _{asdf}_ {xa}")?
} else if xa.is_empty() {
write!(f, "{xb} _{asdf}_ [{xa}]")?
} else {
write!(f, "{xb} _{asdf}_ {xa}")?
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Env {
pub(crate) before: Vec<ParseItem>,
pub(crate) after: Vec<ParseItem>,
pub(crate) center: Option<UnderlineStruct>,
pub(crate) position: Position
}
impl Env {
fn reverse(&mut self) {
for b in &mut self.before { b.reverse(); }
for e in &mut self.after { e.reverse(); }
self.before.reverse();
self.after.reverse();
let temp = self.before.clone();
self.before = self.after.clone();
self.after = temp;
if let Some(cent) = &mut self.center {
cent.reverse()
}
}
pub(crate) fn contains_external(&self) -> bool {
for b in &self.before {
if b.kind == ParseElement::ExtlBound { return true }
}
for a in &self.after {
if a.kind == ParseElement::ExtlBound { return true }
}
false
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct SetChoice {
pub(crate) items: Vec<ParseItem>,
}
impl SetChoice {
pub(crate) fn reverse(&mut self) {
self.items.reverse();
for el in &mut self.items { el.reverse(); }
}
pub(crate) fn contains(&self, element: &ParseElement) -> bool {
for item in &self.items {
if item.kind == *element {
return true
}
}
false
}
fn join_params(&mut self, matrix: &Modifiers, matrix_pos: Position) -> Result<(), RuleSyntaxError> {
let node_mods: Vec<(usize, &Option<ModKind>)> = matrix.nodes.iter().enumerate().filter(|(_, nk)| nk.is_some()).collect();
let feat_mods: Vec<(usize, &Option<ModKind>)> = matrix.feats.iter().enumerate().filter(|(_, fk)| fk.is_some()).collect();
for item in &mut self.items {
match &mut item.kind {
ParseElement::Reference(_, m @ None) | ParseElement::Ipa(_, m @ None) => *m = Some(*matrix),
ParseElement::Reference(_, Some(m)) | ParseElement::Ipa(_, Some(m)) | ParseElement::Matrix(m, _) => {
for (ni, nk) in &node_mods { m.nodes[*ni] = **nk; }
for (fi, fk) in &feat_mods { m.feats[*fi] = **fk; }
if let Some(stress) = matrix.suprs.stress {
m.suprs.stress = Some(stress)
}
if let Some(length) = matrix.suprs.length {
m.suprs.length = Some(length)
}
if let Some(tone) = matrix.suprs.tone {
m.suprs.tone = Some(tone)
}
}
ParseElement::Structure(_, stress, tone, _) | ParseElement::Syllable (stress, tone, _) => {
if !node_mods.is_empty() {
return Err(RuleSyntaxError::SetSyllWrongMods(item.position, matrix_pos, NodeKind::from_usize(node_mods[0].0).as_str()))
}
if !feat_mods.is_empty() {
return Err(RuleSyntaxError::SetSyllWrongMods(item.position, matrix_pos, FeatKind::from_usize(feat_mods[0].0).as_str()))
}
if matrix.suprs.length.is_some() {
return Err(RuleSyntaxError::SetSyllWrongMods(item.position, matrix_pos, "length"))
}
if let Some(str) = matrix.suprs.stress {
*stress = Some(str)
}
if let Some(tn) = matrix.suprs.tone {
*tone = Some(tn)
}
},
ParseElement::ExtlBound | ParseElement::SyllBound | ParseElement::WordBound => return Err(RuleSyntaxError::SetSyllBoundMods(item.position, matrix_pos)),
_ => unreachable!()
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ItemSet {
pub(crate) choices: Vec<SetChoice>,
}
impl ItemSet {
pub(crate) fn reverse(&mut self) {
for choice in &mut self.choices {
choice.reverse();
}
}
#[allow(unused)]
pub(crate) fn contains_at(&self, element: &ParseElement) -> Option<usize> {
for (c, choice) in self.choices.iter().enumerate() {
if choice.contains(element) {
return Some(c)
}
}
None
}
#[allow(unused)]
pub(crate) fn starts_in(&self, el_kind: ParseElement, choice_index: usize) -> bool {
match self.choices.get(choice_index) {
Some(choice) => match choice.items.first() {
Some(item) => item.kind == el_kind,
None => false,
},
None => false,
}
}
pub(crate) fn ends_in(&self, el_kind: ParseElement, choice_index: usize) -> bool {
match self.choices.get(choice_index) {
Some(choice) => match choice.items.last() {
Some(item) => item.kind == el_kind,
None => false,
},
None => false,
}
}
fn join_params(&mut self, matrix: &Modifiers, matrix_pos: Position) -> Result<(), RuleSyntaxError> {
for choice in &mut self.choices {
choice.join_params(matrix, matrix_pos)?
}
Ok(())
}
}
#[doc(hidden)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Reference {
pub(crate) value: usize,
pub(crate) position: Position,
}
#[doc(hidden)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnderlineStruct {
pub(crate) before: Vec<ParseItem>, pub(crate) after: Vec<ParseItem>,
pub(crate) stress: StressMod,
pub(crate) tone: Option<Tone>,
pub(crate) position: Position,
}
impl UnderlineStruct {
fn reverse(&mut self) {
for b in &mut self.before { b.reverse(); }
for e in &mut self.after { e.reverse(); }
self.before.reverse();
self.after.reverse();
let temp = self.before.clone();
self.before = self.after.clone();
self.after = temp;
}
}
type RefAssign = Option<usize>;
type StressMod = Option<SpecMod>;
type OptMin = usize;
type OptMax = usize;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum ParseElement {
EmptySet , ExtlBound , WordBound , SyllBound , Metathesis , MetaOrdered, Ellipsis , OptEllipsis, Set (ItemSet),
Ipa (Segment, Option<Modifiers>),
Matrix (Modifiers, RefAssign),
Syllable (StressMod, Option<Tone>, RefAssign),
Structure(Vec<ParseItem>, StressMod, Option<Tone>, RefAssign),
Optional (Vec<ParseItem>, OptMin, OptMax),
Reference(Reference, Option<Modifiers>),
}
impl ParseElement {
fn as_matrix(&self) -> Option<&Modifiers> {
if let Self::Matrix(v, _) = self {
Some(v)
} else {
None
}
}
fn reverse(&mut self) {
match self {
Self::EmptySet | Self::WordBound | Self::SyllBound |
Self::Ellipsis | Self::Metathesis | Self::Ipa(..) |
Self::Matrix(..) | Self::Reference(..) | Self::Syllable(..) |
Self::MetaOrdered | Self::OptEllipsis | Self::ExtlBound => {},
Self::Optional(items, ..) | Self::Structure(items, ..) => {
items.reverse();
for i in items { i.reverse(); }
},
Self::Set(set) => { set.reverse(); }
}
}
}
impl fmt::Display for ParseElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Reference(tk, p) => {
write!(f, "{tk:#?} = {p:#?}")
},
Self::EmptySet => write!(f, "∅"),
Self::ExtlBound => write!(f, "##"),
Self::WordBound => write!(f, "#"),
Self::SyllBound => write!(f, "$"),
Self::Ellipsis => write!(f, "…"),
Self::OptEllipsis => write!(f, "(…)"),
Self::Metathesis => write!(f, "&"),
Self::MetaOrdered => write!(f, "@"),
Self::Ipa(s, m) => write!(f, "{s:?} + {m:?}"),
Self::Matrix(tokens, refr) => {
write!(f, "{tokens:#?}={refr:#?}")
},
Self::Syllable(str, tone, refr) => {
write!(f, "SYLL=>{str:?}:{tone:#?}={refr:#?}")
},
Self::Structure(segs, str, tone, refr) => {
write!(f, "STRUCT=>{str:?}:{tone:#?}={refr:#?} <")?;
for i in segs {
write!(f, "{i}")?;
write!(f, ", ")?;
}
write!(f, ">")
}
Self::Set(s) => {
write!(f, "{{ ")?;
for c in &s.choices {
for i in &c.items {
write!(f, "{i}")?;
}
write!(f, ", ")?;
}
write!(f, " }}")
},
Self::Optional(s, min, max) => {
write!(f, "(")?;
for i in s {
write!(f, "{i}")?;
write!(f, ", ")?;
}
write!(f, " {min}:{max})")
},
}
}
}
#[doc(hidden)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseItem {
pub(crate) kind: ParseElement,
pub(crate) position: Position,
}
impl ParseItem {
pub(crate) fn new(k: ParseElement, p: Position) -> Self {
Self { kind: k, position: p }
}
pub(crate) fn reverse(&mut self) {
self.kind.reverse();
}
pub(crate) fn is_opt_and_nullable(&self) -> bool {
match self.kind {
ParseElement::Optional(_, min, _) => min == 0,
_ => false
}
}
}
impl fmt::Display for ParseItem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.kind)
}
}
pub(crate) struct Parser {
token_list: Vec<Token>,
group: usize,
line: usize,
pos: usize,
curr_tkn: Token,
contains_external_in_input: bool,
contains_external_in_env: bool,
}
impl Parser {
pub(crate) fn new(token_list: Vec<Token>, group: usize, line: usize) -> Self {
let curr_tkn = token_list[0].clone();
Self {
token_list,
group,
line,
pos: 0,
curr_tkn,
contains_external_in_input: false,
contains_external_in_env: false,
}
}
fn advance(&mut self) {
self.pos += 1;
self.curr_tkn = if self.has_more_tokens() && self.curr_tkn.kind != TokenKind::Comment {
self.token_list[self.pos].clone()
} else {
Token { kind: TokenKind::Eol, value: Arc::default(), position: Position::new(self.group, self.line, self.pos, self.pos+1) }
};
}
fn has_more_tokens(&self) -> bool { self.pos < self.token_list.len() }
fn peek_expect(&self, knd: TokenKind) -> bool { self.curr_tkn.kind == knd }
fn expect(&mut self, knd: TokenKind) -> bool {
if self.curr_tkn.kind == knd {
self.advance();
true
} else {
false
}
}
fn eat(&mut self) -> Token {
let token = self.curr_tkn.clone();
self.advance();
token
}
fn eat_expect(&mut self, knd: TokenKind) -> Option<Token> {
if self.curr_tkn.kind == knd {
Some(self.eat())
} else {
None
}
}
fn get_bound(&mut self) -> Option<ParseItem> {
if let Some(token) = self.eat_expect(TokenKind::SyllBoundary) {
return Some(ParseItem::new(ParseElement::SyllBound, token.position))
}
if let Some(token) = self.eat_expect(TokenKind::WordBoundary) {
return Some(ParseItem::new(ParseElement::WordBound, token.position))
}
None
}
fn get_word_bound(&mut self) -> Option<ParseItem> {
if let Some(token) = self.eat_expect(TokenKind::WordBoundary) {
return Some(ParseItem::new(ParseElement::WordBound, token.position))
}
None
}
fn get_syll_bound(&mut self) -> Option<ParseItem> {
if let Some(token) = self.eat_expect(TokenKind::SyllBoundary) {
return Some(ParseItem::new(ParseElement::SyllBound, token.position))
}
None
}
fn get_x_bound(&mut self) -> Option<ParseItem> {
if let Some(token) = self.eat_expect(TokenKind::ExternBoundary) {
return Some(ParseItem::new(ParseElement::ExtlBound, token.position))
}
None
}
fn get_env_elements(&mut self, is_after: bool) -> Result<(Vec<ParseItem>, Option<usize>), RuleSyntaxError> {
let mut els = Vec::new();
let mut contains_word_bound = false;
let mut word_bound_pos = Position::new(0, 0, 0, 0);
let mut underline_in_struct = None;
loop {
if let Some(x) = self.try_spec_struct(&mut underline_in_struct)? {
if underline_in_struct.is_some() {
if is_after {
return Err(RuleSyntaxError::TooManyUnderlinesStruct(x.position))
}
els.push(x);
break;
}
els.push(x);
continue;
}
if let Some(x) = self.get_word_bound() {
word_bound_pos = x.position;
if contains_word_bound {
return Err(RuleSyntaxError::TooManyWordBoundaries(word_bound_pos))
}
els.push(x);
contains_word_bound = true;
continue;
}
if let Some(x) = self.get_x_bound() {
els.push(x);
self.contains_external_in_env = true;
continue;
}
if let Some(x) = self.get_syll_bound() {
els.push(x);
continue;
}
if let Some(el) = self.eat_expect(TokenKind::WrappedEllipsis) {
els.push(ParseItem::new(ParseElement::OptEllipsis, el.position));
continue;
}
if let Some(el) = self.eat_expect(TokenKind::Ellipsis) {
els.push(ParseItem::new(ParseElement::Ellipsis, el.position));
continue;
}
if let Some(x) = self.get_opt()? {
els.push(x);
continue;
}
if let Some(x) = self.get_term()? {
els.push(x);
continue;
}
break;
}
if contains_word_bound {
if !is_after && els.first().expect("contains wbound").kind != ParseElement::WordBound {
return Err(RuleSyntaxError::StuffBeforeWordBound(word_bound_pos))
} else if is_after && els.last().expect("contains wbound").kind != ParseElement::WordBound {
return Err(RuleSyntaxError::StuffAfterWordBound(word_bound_pos))
}
}
Ok((els, underline_in_struct))
}
fn into_underline_struct(parse_items: &mut Vec<ParseItem>, has_underline_struct: Option<usize>) -> Option<UnderlineStruct> {
let pos = has_underline_struct?;
let last_item = parse_items.pop().expect("parse_items is not empty");
let ParseElement::Structure(vec,stress,tone,_) = last_item.kind else { unreachable!() };
let (before, after) = vec.split_at(pos);
Some(UnderlineStruct {
before: before.iter().rev().cloned().collect(),
after: after.to_vec(),
stress,
tone,
position: last_item.position
})
}
fn get_env_stat(&mut self) -> Result<Env, RuleSyntaxError> {
let start = self.curr_tkn.position.start;
let (mut before, has_underline_struct) = self.get_env_elements(false)?;
if has_underline_struct.is_none() && !self.expect(TokenKind::Underline) {
return Err(RuleSyntaxError::ExpectedUnderline(self.curr_tkn.clone()))
}
let (after, _) = self.get_env_elements(true)?;
if self.peek_expect(TokenKind::Underline) {
return Err(RuleSyntaxError::TooManyUnderlines(self.curr_tkn.clone()))
}
let end = self.token_list[self.pos-1].position.end;
let center = Self::into_underline_struct(&mut before, has_underline_struct);
Ok(Env{ before, after, center, position: Position::new(self.group, self.line, start, end)})
}
fn get_env_spec(&mut self) -> Result<Option<Vec<EnvItem>>, RuleSyntaxError> {
let start = self.curr_tkn.position.start;
let pstn = self.pos;
if !self.expect(TokenKind::Underline) {
return Ok(None)
}
debug_assert_eq!(pstn, self.pos - 1);
if !self.expect(TokenKind::Comma) {
self.pos = pstn;
self.curr_tkn = self.token_list[self.pos].clone();
return Ok(None)
}
let (items, has_underline_struct) = self.get_env_elements(false)?;
if has_underline_struct.is_some() {
return Err(RuleSyntaxError::TooManyUnderlinesStruct(items.last().expect("items is not empty").position))
}
if self.expect(TokenKind::Underline) {
self.pos = pstn;
self.curr_tkn = self.token_list[self.pos].clone();
return Ok(None)
}
let end = self.token_list[self.pos-1].position.end;
let position = Position::new(self.group, self.line, start, end);
let v = vec![
EnvItem { envs: vec![Env { before: items.clone(), after: Vec::new(), center: None, position}], position },
EnvItem { envs: vec![Env { before: Vec::new(), after: items.into_iter().rev().collect(), center: None, position}], position },
];
Ok(Some(v))
}
fn get_envs(&mut self) -> Result<EnvItem, RuleSyntaxError> {
let start = self.curr_tkn.position.start;
if !self.expect(TokenKind::LeftColCurly) {
let env: Env = self.get_env_stat()?;
return Ok(EnvItem{envs: vec![env.clone()], position: env.position})
}
let mut envs = Vec::with_capacity(2);
envs.push(self.get_env_stat()?);
loop {
if self.expect(TokenKind::RightColCurly) { break; }
if !self.expect(TokenKind::Comma) {
return Err(RuleSyntaxError::ExpectedComma(self.curr_tkn.clone()))
}
let x = self.get_env_stat()?;
envs.push(x);
}
let end = self.token_list[self.pos-1].position.end;
Ok(EnvItem { envs, position: Position::new(self.group, self.line, start, end) })
}
fn get_env_expr(&mut self) -> Result<Vec<EnvItem>, RuleSyntaxError> {
if let Some(s) = self.get_env_spec()? { return Ok(s) }
let mut envs = Vec::new();
loop {
let x = self.get_envs()?;
envs.push(x);
if !self.expect(TokenKind::Comma) {
break
}
}
if envs.is_empty() { return Err(RuleSyntaxError::EmptyEnv(self.group, self.line, self.token_list[self.pos].position.start)) }
Ok(envs)
}
fn get_except_block(&mut self) -> Result<Vec<EnvItem>, RuleSyntaxError> {
if !self.expect(TokenKind::Pipe) { return Ok(Vec::new()) }
self.get_env_expr()
}
fn get_context_block(&mut self) -> Result<Vec<EnvItem>, RuleSyntaxError> {
if !self.expect(TokenKind::Slash) { return Ok(Vec::new()) }
self.get_env_expr()
}
fn join_group_with_params(&self, character: ParseItem, parameters: ParseItem) -> ParseItem {
let mut chr = *character.kind.as_matrix().expect("Caller asserts `character` is a matrix");
let params = parameters.kind.as_matrix().expect("Caller asserts `parameters` is a matrix");
for (i, n) in params.nodes.iter().enumerate() {
if n.is_none() { continue }
chr.nodes[i] = *n
}
for (i, f) in params.feats.iter().enumerate() {
if f.is_none() { continue }
chr.feats[i] = *f
}
chr.suprs.stress = params.suprs.stress;
chr.suprs.length = params.suprs.length;
chr.suprs.tone = params.suprs.tone;
ParseItem::new(ParseElement::Matrix(chr, None), Position::new(self.group, self.line, character.position.start, parameters.position.end ))
}
fn ipa_to_vals(ipa: Token) -> Result<Segment, RuleSyntaxError> {
match CARDINALS_MAP.get(ipa.value.as_ref()) {
Some(z) => Ok(*z),
None => Err(RuleSyntaxError::UnknownIPA(ipa))
}
}
fn group_to_matrix(&self, chr: &Token) -> Result<ParseItem, RuleSyntaxError> {
use FeatKind::*;
use ModKind::*;
const SYLL_M: (FeatKind, ModKind) = (Syllabic, Binary(BinMod::Negative)); const SYLL_P: (FeatKind, ModKind) = (Syllabic, Binary(BinMod::Positive)); const CONS_M: (FeatKind, ModKind) = (Consonantal, Binary(BinMod::Negative)); const CONS_P: (FeatKind, ModKind) = (Consonantal, Binary(BinMod::Positive)); const SONR_M: (FeatKind, ModKind) = (Sonorant, Binary(BinMod::Negative)); const SONR_P: (FeatKind, ModKind) = (Sonorant, Binary(BinMod::Positive)); const APPR_M: (FeatKind, ModKind) = (Approximant, Binary(BinMod::Negative)); const APPR_P: (FeatKind, ModKind) = (Approximant, Binary(BinMod::Positive)); const CONT_M: (FeatKind, ModKind) = (Continuant, Binary(BinMod::Negative)); const CONT_P: (FeatKind, ModKind) = (Continuant, Binary(BinMod::Positive)); const DLRL_M: (FeatKind, ModKind) = (DelayedRelease, Binary(BinMod::Negative)); const NASL_P: (FeatKind, ModKind) = (Nasal, Binary(BinMod::Positive));
let mut args = Modifiers::new();
(match chr.value.as_ref() {
"C" => vec![CONS_P, SYLL_M], "O" => vec![CONS_P, SONR_M, SYLL_M], "S" => vec![CONS_P, SONR_P, SYLL_M], "P" => vec![CONS_P, SONR_M, SYLL_M, DLRL_M, CONT_M], "F" => vec![CONS_P, SONR_M, SYLL_M, APPR_M, CONT_P], "L" => vec![CONS_P, SONR_P, SYLL_M, APPR_P], "N" => vec![CONS_P, SONR_P, SYLL_M, APPR_M, NASL_P], "G" => vec![CONS_M, SONR_P, SYLL_M], "V" => vec![CONS_M, SONR_P, SYLL_P],
_ => return Err(RuleSyntaxError::UnknownGrouping(chr.clone())),
}).into_iter().for_each(|(feature, value)| {
args.feats[feature as usize] = Some(value)
});
Ok(ParseItem::new(ParseElement::Matrix(args, None), Position::new(self.group, self.line, chr.position.start, chr.position.end )))
}
fn is_feature(&self) -> bool{ matches!(self.curr_tkn.kind, TokenKind::Feature(_)) }
fn curr_token_to_modifier(&self) -> Result<(FeatureCategory, Mods), RuleSyntaxError> {
match self.curr_tkn.kind {
TokenKind::Feature(feature) => {
let value = &self.curr_tkn.value;
match value.as_ref() {
"+" => Ok((feature, Mods::Binary(BinMod::Positive))),
"-" => Ok((feature, Mods::Binary(BinMod::Negative))),
"α"|"β"|"γ"|"δ"|"ε"|"ζ"|"η"|"θ"|"ι"|"κ"|"λ"|"μ"|"ν"|"ξ"|"ο"|"π"|"ρ"|"σ"|"ς"|"τ"|"υ"|"φ"|"χ"|"ψ"|"ω"|
"A"|"B"|"C"|"D"|"E"|"F"|"G"|"H"|"I"|"J"|"K"|"L"|"M"|"N"|"O"|"P"|"Q"|"R"|"S"|"T"|"U"|"V"|"W"|"X"|"Y"|"Z" =>
Ok((feature, Mods::Alpha(AlphaMod::Alpha(value.chars().next().unwrap())))),
"-α"|"-β"|"-γ"|"-δ"|"-ε"|"-ζ"|"-η"|"-θ"|"-ι"|"-κ"|"-λ"|"-μ"|"-ν"|"-ξ"|"-ο"|"-π"|"-ρ"|"-σ"|"-ς"|"-τ"|"-υ"|"-φ"|"-χ"|"-ψ"|"-ω"|
"-A"|"-B"|"-C"|"-D"|"-E"|"-F"|"-G"|"-H"|"-I"|"-J"|"-K"|"-L"|"-M"|"-N"|"-O"|"-P"|"-Q"|"-R"|"-S"|"-T"|"-U"|"-V"|"-W"|"-X"|"-Y"|"-Z" =>
Ok((feature, Mods::Alpha(AlphaMod::InvAlpha(value.chars().nth(1).unwrap())))),
_ if feature == FeatureCategory::Supr(SupraKind::Tone) => {
let v = value.replace('0', "");
if v.chars().count() > 4 {
Err(RuleSyntaxError::ToneTooBig(self.curr_tkn.clone()))
} else {
Ok((feature, Mods::Number(v.parse().unwrap_or(0))))
}
},
_ => {
unreachable!();
}
}
},
_ => unreachable!(),
}
}
fn get_param_args(&mut self, is_syll: bool) -> Result<Modifiers, RuleSyntaxError> {
let mut args = Modifiers::new();
let mut length_mods: [Option<(ModKind, Position)>; 3] = [None; 3];
let mut stress_mods: [Option<(ModKind, Position)>; 3] = [None; 3];
while self.has_more_tokens() {
if self.expect(TokenKind::RightSquare) {
break;
}
if self.expect(TokenKind::Comma) {
continue;
}
if self.is_feature() {
let (ft, mk) = self.curr_token_to_modifier()?;
if ft != FeatureCategory::Supr(SupraKind::Tone) && ft != FeatureCategory::Supr(SupraKind::Stress) && ft != FeatureCategory::Supr(SupraKind::SecStress) && is_syll {
return Err(RuleSyntaxError::BadSyllableMatrix(self.curr_tkn.clone()))
}
match ft {
FeatureCategory::Node(t) => args.nodes[t as usize] = match mk {
Mods::Binary(b) => Some(ModKind::Binary(b)),
Mods::Alpha(a) => Some(ModKind::Alpha(a)),
Mods::Number(_) => unreachable!(),
},
FeatureCategory::Feat(t) => args.feats[t as usize] = match mk {
Mods::Binary(b) => Some(ModKind::Binary(b)),
Mods::Alpha(a) => Some(ModKind::Alpha(a)),
Mods::Number(_) => unreachable!(),
},
FeatureCategory::Supr(t) => match mk {
Mods::Number(n) => args.suprs.tone = Some(n),
Mods::Alpha(a) => match t {
SupraKind::Long => length_mods[0] = Some((ModKind::Alpha(a), self.curr_tkn.position)),
SupraKind::Overlong => length_mods[1] = Some((ModKind::Alpha(a), self.curr_tkn.position)),
SupraKind::LengthPair => length_mods[2] = Some((ModKind::Alpha(a), self.curr_tkn.position)),
SupraKind::Stress => stress_mods[0] = Some((ModKind::Alpha(a), self.curr_tkn.position)),
SupraKind::SecStress => stress_mods[1] = Some((ModKind::Alpha(a), self.curr_tkn.position)),
SupraKind::StressPair => stress_mods[2] = Some((ModKind::Alpha(a), self.curr_tkn.position)),
SupraKind::Tone => unreachable!("Tone cannot be `Alpha'd` (yet anyway)"),
},
Mods::Binary(b) => match t {
SupraKind::Long => length_mods[0] = Some((ModKind::Binary(b), self.curr_tkn.position)),
SupraKind::Overlong => length_mods[1] = Some((ModKind::Binary(b), self.curr_tkn.position)),
SupraKind::Stress => stress_mods[0] = Some((ModKind::Binary(b), self.curr_tkn.position)),
SupraKind::SecStress => stress_mods[1] = Some((ModKind::Binary(b), self.curr_tkn.position)),
SupraKind::LengthPair => unreachable!("Length cannot be `+/-`"),
SupraKind::StressPair => unreachable!("StressPair cannot be `+/-`"),
SupraKind::Tone => unreachable!("Tone cannot be `+/-`"),
},
}
}
self.advance();
continue;
}
if self.curr_tkn.kind == TokenKind::Eol {
return Err(RuleSyntaxError::UnexpectedEol(self.curr_tkn.clone(), ']'))
}
return Err(RuleSyntaxError::ExpectedTokenFeature(self.curr_tkn.clone()))
}
args.suprs.length = match length_mods {
[Some((_, i)), .., Some((_, j))] | [.., Some((_, i)), Some((_, j))] => {
match i.start.cmp(&j.start) {
std::cmp::Ordering::Less => return Err(RuleSyntaxError::SupraConflict(i, j)),
std::cmp::Ordering::Greater => return Err(RuleSyntaxError::SupraConflict(j, i)),
std::cmp::Ordering::Equal => unreachable!("Two tokens cannot have the same position"),
}
},
[None, None, None] => None,
[Some(l), None, None] => Some(SpecMod::First(l.0)),
[None, Some(o), None] => Some(SpecMod::Second(o.0)),
[None, None, Some(j)] => Some(SpecMod::Joined(j.0)),
[Some(l), Some(o), None] => Some(SpecMod::Both(l.0, o.0)),
};
args.suprs.stress = match stress_mods {
[Some((_, i)), .., Some((_, j))] | [.., Some((_, i)), Some((_, j))] => {
match i.start.cmp(&j.start) {
std::cmp::Ordering::Less => return Err(RuleSyntaxError::SupraConflict(i, j)),
std::cmp::Ordering::Greater => return Err(RuleSyntaxError::SupraConflict(j, i)),
std::cmp::Ordering::Equal => unreachable!("Two tokens cannot have the same position"),
}
},
[None, None, None] => None,
[Some(p), None, None] => Some(SpecMod::First(p.0)),
[None, Some(s), None] => Some(SpecMod::Second(s.0)),
[None, None, Some(j)] => Some(SpecMod::Joined(j.0)),
[Some(p), Some(s), None] => Some(SpecMod::Both(p.0, s.0)),
};
Ok(args)
}
fn get_params(&mut self) -> Result<ParseItem, RuleSyntaxError> {
let start = self.token_list[self.pos-1].position.start;
let args = self.get_param_args(false)?;
let end = self.token_list[self.pos-1].position.end;
Ok(ParseItem::new(ParseElement::Matrix(args, None), Position::new(self.group, self.line, start, end)))
}
fn get_group(&mut self) -> Result<ParseItem, RuleSyntaxError> {
let chr = self.group_to_matrix(&self.curr_tkn)?;
self.advance();
if !self.expect(TokenKind::Colon) {
return Ok(chr)
}
if !self.expect(TokenKind::LeftSquare) {
return Err(RuleSyntaxError::ExpectedMatrix(self.curr_tkn.clone()))
}
let params = self.get_params()?;
let joined_matrix = self.join_group_with_params(chr, params);
Ok(joined_matrix)
}
fn get_ipa(&mut self) -> Result<ParseItem, RuleSyntaxError> {
let mut ipa = Self::ipa_to_vals(self.curr_tkn.clone())?;
let pos = self.curr_tkn.position;
self.advance();
while matches!(self.curr_tkn.kind, TokenKind::Diacritic(_)) {
let dia = self.eat();
let d = dia.kind.as_diacritic().unwrap();
if let Err((mod_index, is_node)) = ipa.check_and_apply_diacritic(&DIACRITS[*d as usize]) {
if !is_node {
let ft = FeatKind::from_usize(mod_index);
let positive = match &DIACRITS[*d as usize].prereqs.feats[mod_index].unwrap() {
ModKind::Binary(bin_mod) => *bin_mod == BinMod::Positive,
_ => unreachable!(),
};
return Err(RuleSyntaxError::DiacriticDoesNotMeetPreReqsFeat(pos, dia.position, ft.to_string(), positive))
} else {
let nt = NodeKind::from_usize(mod_index);
let positive = match &DIACRITS[*d as usize].prereqs.nodes[mod_index].unwrap() {
ModKind::Binary(bin_mod) => *bin_mod == BinMod::Positive,
_ => unreachable!(),
};
return Err(RuleSyntaxError::DiacriticDoesNotMeetPreReqsNode(pos, dia.position, nt.to_string(), positive))
};
}
}
if let Some(eq) = self.eat_expect(TokenKind::Equals) {
return Err(RuleSyntaxError::IPACannotBeRefd(eq))
}
if !self.expect(TokenKind::Colon) {
return Ok(ParseItem::new(ParseElement::Ipa(ipa, None), Position::new(self.group, self.line, pos.start, self.token_list[self.pos-1].position.end)))
}
if !self.expect(TokenKind::LeftSquare) {
return Err(RuleSyntaxError::ExpectedMatrix(self.curr_tkn.clone()))
}
let params = self.get_params()?;
let joined_kind = ParseElement::Ipa(ipa, Some(*params.kind.as_matrix().unwrap()));
if let Some(eq) = self.eat_expect(TokenKind::Equals) {
return Err(RuleSyntaxError::IPACannotBeRefd(eq))
}
Ok(ParseItem::new(joined_kind, Position::new(self.group, self.line, pos.start, params.position.end )))
}
fn get_ref_assign(&mut self, number: Token, char: &ParseItem) -> ParseItem {
let num = number.value.parse::<usize>().expect("number should be a number as set in `self.get_seg`");
let mods = char.kind.as_matrix().expect("char should be matrix as set in `self.get_group`");
ParseItem::new(ParseElement::Matrix(*mods, Some(num)), Position::new(self.group, self.line, char.position.start, char.position.end ))
}
fn get_seg(&mut self) -> Result<Option<ParseItem>, RuleSyntaxError> {
if self.peek_expect(TokenKind::Cardinal) {
return Ok(Some(self.get_ipa()?))
}
if self.peek_expect(TokenKind::Group) {
let chr = self.get_group()?;
if self.expect(TokenKind::Equals) {
let Some(n) = self.eat_expect(TokenKind::Number) else {
return Err(RuleSyntaxError::ExpectedReference(self.curr_tkn.clone()))
};
let res = self.get_ref_assign(n, &chr);
return Ok(Some(res))
}
return Ok(Some(chr))
}
if self.expect(TokenKind::LeftSquare) {
let params = self.get_params()?;
if self.expect(TokenKind::Equals) {
let Some(n) = self.eat_expect(TokenKind::Number) else {
return Err(RuleSyntaxError::ExpectedReference(self.curr_tkn.clone()))
};
let res = self.get_ref_assign(n, ¶ms);
return Ok(Some(res))
}
return Ok(Some(params))
}
Ok(None)
}
fn diacritics_as_params(diacrits: &[&Diacritic]) -> Result<Modifiers, RuleSyntaxError> {
let mut args = Modifiers::new();
for diacritic in diacrits {
for (ni, node) in diacritic.payload.nodes.iter().enumerate() {
if node.is_some() {
args.nodes[ni] = *node;
}
}
for (fi, feat) in diacritic.payload.feats.iter().enumerate() {
if feat.is_some() {
args.feats[fi] = *feat;
}
}
}
Ok(args)
}
fn get_ref(&mut self) -> Result<Option<ParseItem>, RuleSyntaxError> {
let Some(Token { value, position, .. }) = self.eat_expect(TokenKind::Number) else { return Ok(None) };
let refr = Reference { value: value.parse().unwrap(), position };
let mut pos = refr.position;
if matches!(self.curr_tkn.kind, TokenKind::Diacritic(_)) {
let mut diacrits = vec![];
let mut end = pos.end;
while matches!(self.curr_tkn.kind, TokenKind::Diacritic(_)) {
let dia = self.eat();
end = dia.position.end;
diacrits.push(&DIACRITS[*dia.kind.as_diacritic().unwrap() as usize]);
}
let params = Self::diacritics_as_params(&diacrits)?;
pos.end = end;
return Ok(Some(ParseItem::new(ParseElement::Reference(refr, Some(params)), pos)))
}
if !self.expect(TokenKind::Colon) {
return Ok(Some(ParseItem::new(ParseElement::Reference(refr, None), pos)))
}
if !self.expect(TokenKind::LeftSquare) {
return Err(RuleSyntaxError::ExpectedMatrix(self.curr_tkn.clone()))
}
let params = self.get_params()?;
let matrix = params.kind.as_matrix().expect("params should be matrix as set in `self.get_params`");
pos.end = params.position.end;
Ok(Some(ParseItem::new(ParseElement::Reference(refr, Some(*matrix)), pos)))
}
fn get_opt(&mut self) -> Result<Option<ParseItem>, RuleSyntaxError> {
let start_pos = self.curr_tkn.position.start;
if !self.expect(TokenKind::LeftBracket) { return Ok(None) }
let mut segs = Vec::new();
let mut first_bound: usize = 0;
let mut second_bound: usize = 0;
while self.has_more_tokens() {
if self.peek_expect(TokenKind::RightBracket) { break; }
if let Some(x) = self.get_x_bound() { segs.push(x); continue; }
if let Some(x) = self.get_bound() { segs.push(x); continue; }
if let Some(x) = self.get_syll()? { segs.push(x); continue; }
if let Some(x) = self.get_set()? { segs.push(x); continue; }
if let Some(x) = self.get_seg()? { segs.push(x); continue; }
if let Some(x) = self.get_ref()? { segs.push(x); continue; }
if self.peek_expect(TokenKind::Comma) { break; }
return Err(RuleSyntaxError::ExpectedSegment(self.curr_tkn.clone()))
}
if self.expect(TokenKind::RightBracket) {
let end_pos = self.token_list[self.pos-1].position.end;
return Ok(Some(ParseItem::new(ParseElement::Optional(segs, 0, 1), Position::new(self.group, self.line, start_pos, end_pos))))
}
if !self.expect(TokenKind::Comma) {
return Err(RuleSyntaxError::ExpectedComma(self.curr_tkn.clone()))
}
if let Some(number) = self.eat_expect(TokenKind::Number) {
first_bound = number.value.parse().unwrap();
}
if self.expect(TokenKind::RightBracket) {
let end_pos = self.token_list[self.pos-1].position.end;
return Ok(Some(ParseItem::new(ParseElement::Optional(segs, 0, first_bound), Position::new(self.group, self.line, start_pos, end_pos))))
}
if !self.expect(TokenKind::Colon) {
return Err(RuleSyntaxError::ExpectedColon(self.curr_tkn.clone()))
}
if let Some(number) = self.eat_expect(TokenKind::Number) {
second_bound = number.value.parse().unwrap();
if second_bound < first_bound {
return Err(RuleSyntaxError::OptMathError(number, first_bound, second_bound))
}
}
if self.expect(TokenKind::RightBracket) {
let end_pos = self.token_list[self.pos-1].position.end;
return Ok(Some(ParseItem::new(ParseElement::Optional(segs, first_bound, second_bound), Position::new(self.group, self.line, start_pos, end_pos))))
}
Err(RuleSyntaxError::ExpectedRightBracket(self.curr_tkn.clone()))
}
fn get_set_choices(&mut self) -> Result<SetChoice, RuleSyntaxError> {
let mut items = Vec::new();
while self.has_more_tokens()
&& !self.peek_expect(TokenKind::RightCurly)
&& !self.peek_expect(TokenKind::Comma) {
if let Some(x) = self.get_ref()? { items.push(x); continue; }
if let Some(x) = self.get_seg()? { items.push(x); continue; }
if let Some(x) = self.get_bound() { items.push(x); continue; }
if let Some(x) = self.get_syll()? { items.push(x); continue; }
if let Some(x) = self.get_struct()? { items.push(x); continue; }
return Err(RuleSyntaxError::ExpectedSegment(self.curr_tkn.clone()))
}
Ok(SetChoice { items })
}
fn get_set(&mut self) -> Result<Option<ParseItem>, RuleSyntaxError> {
let start_pos = self.curr_tkn.position.start;
if !self.expect(TokenKind::LeftCurly) { return Ok(None) }
let mut terms = Vec::new();
while self.has_more_tokens() {
if self.expect(TokenKind::RightCurly) { break; }
if !terms.is_empty() && !self.expect(TokenKind::Comma) {
return Err(RuleSyntaxError::ExpectedComma(self.curr_tkn.clone()))
}
let choice = self.get_set_choices()?;
if !choice.items.is_empty() {
terms.push(choice);
} else if self.expect(TokenKind::RightCurly) { break;
} else {
return Err(RuleSyntaxError::ExpectedSegment(self.curr_tkn.clone()))
}
}
let end_pos = self.token_list[self.pos-1].position.end;
let mut pos = Position::new(self.group, self.line, start_pos, end_pos);
if terms.is_empty() {
return Err(RuleSyntaxError::EmptySet(pos))
}
if !self.expect(TokenKind::Colon) {
return Ok(Some(ParseItem::new(ParseElement::Set(ItemSet{choices: terms.clone()}), pos)))
}
if !self.expect(TokenKind::LeftSquare) {
return Err(RuleSyntaxError::ExpectedMatrix(self.curr_tkn.clone()))
}
let params = self.get_params()?;
let matrix = params.kind.as_matrix().expect("params should be matrix as set in `self.get_params`");
pos.end = params.position.end;
let mut set = ItemSet { choices: terms.clone() };
set.join_params(matrix, params.position)?;
Ok(Some(ParseItem::new(ParseElement::Set(set), pos)))
}
fn get_syll(&mut self) -> Result<Option<ParseItem>, RuleSyntaxError> {
let start_pos = self.curr_tkn.position.start;
if !self.expect(TokenKind::Syllable) { return Ok(None) }
if !self.expect(TokenKind::Colon) {
if self.expect(TokenKind::Equals) {
let Some(number) = self.eat_expect(TokenKind::Number) else {
return Err(RuleSyntaxError::ExpectedReference(self.curr_tkn.clone()))
};
let num = number.value.parse::<usize>().unwrap();
let end_pos = self.token_list[self.pos-1].position.end;
return Ok(Some(ParseItem::new(ParseElement::Syllable(None, None, Some(num)), Position::new(self.group, self.line, start_pos, end_pos))))
}
let end_pos = self.token_list[self.pos-1].position.end;
return Ok(Some(ParseItem::new(ParseElement::Syllable(None, None, None), Position::new(self.group, self.line, start_pos, end_pos))))
}
if !self.expect(TokenKind::LeftSquare) {
return Err(RuleSyntaxError::ExpectedMatrix(self.curr_tkn.clone()))
}
let mods = self.get_param_args(true)?;
let end_pos = self.token_list[self.pos-1].position.end;
if self.expect(TokenKind::Equals) {
let Some(number) = self.eat_expect(TokenKind::Number) else {
return Err(RuleSyntaxError::ExpectedReference(self.curr_tkn.clone()))
};
let num = number.value.parse::<usize>().unwrap();
return Ok(Some(ParseItem::new(ParseElement::Syllable(mods.suprs.stress, mods.suprs.tone, Some(num)), Position::new(self.group, self.line, start_pos, end_pos))))
}
Ok(Some(ParseItem::new(ParseElement::Syllable(mods.suprs.stress, mods.suprs.tone, None), Position::new(self.group, self.line, start_pos, end_pos))))
}
fn get_syll_term(&mut self) -> Result<Option<ParseItem>, RuleSyntaxError> {
if let Some(item) = self.get_seg()? {
Ok(Some(item))
}
else if let Some(el) = self.eat_expect(TokenKind::WrappedEllipsis) {
Ok(Some(ParseItem::new(ParseElement::OptEllipsis, el.position)))
}
else if let Some(el) = self.eat_expect(TokenKind::Ellipsis) {
Ok(Some(ParseItem::new(ParseElement::Ellipsis, el.position)))
}
else if let Some(item) = self.get_ref()? {
Ok(Some(item))
}
else if let Some(item) = self.get_set()? {
Ok(Some(item))
}
else if let Some(item) = self.get_opt()? {
Ok(Some(item))
} else {
Ok(None)
}
}
fn try_spec_struct(&mut self, has_underline: &mut Option<usize>) -> Result<Option<ParseItem>, RuleSyntaxError> {
let start_pos = self.curr_tkn.position.start;
if !self.expect(TokenKind::LeftAngle) { return Ok(None) }
let mut terms = Vec::new();
while self.has_more_tokens() {
if self.expect(TokenKind::RightAngle) { break; }
if let Some(item) = self.get_syll_term()? {
terms.push(item);
continue;
}
if let Some(el) = self.eat_expect(TokenKind::Underline) {
if has_underline.is_some() {
return Err(RuleSyntaxError::TooManyUnderlines(el.clone()))
}
*has_underline = Some(terms.len());
continue;
}
return Err(RuleSyntaxError::ExpectedStructElem(self.curr_tkn.clone()))
}
if !self.expect(TokenKind::Colon) {
if self.expect(TokenKind::Equals) {
if has_underline.is_some() {
return Err(RuleSyntaxError::StructCannotBeRefd(self.token_list[self.pos - 1].clone()))
}
let Some(number) = self.eat_expect(TokenKind::Number) else {
return Err(RuleSyntaxError::ExpectedReference(self.curr_tkn.clone()))
};
let num = number.value.parse::<usize>().unwrap();
let end_pos = self.token_list[self.pos-1].position.end;
return Ok(Some(ParseItem::new(ParseElement::Structure(terms, None, None, Some(num)), Position::new(self.group, self.line, start_pos, end_pos))))
}
let end_pos = self.token_list[self.pos-1].position.end;
return Ok(Some(ParseItem::new(ParseElement::Structure(terms, None, None, None), Position::new(self.group, self.line, start_pos, end_pos))))
}
if !self.expect(TokenKind::LeftSquare) {
return Err(RuleSyntaxError::ExpectedMatrix(self.curr_tkn.clone()))
}
let mods = self.get_param_args(true)?;
let end_pos = self.token_list[self.pos-1].position.end;
if self.expect(TokenKind::Equals) {
if has_underline.is_some() {
return Err(RuleSyntaxError::StructCannotBeRefd(self.token_list[self.pos - 1].clone()))
}
let Some(number) = self.eat_expect(TokenKind::Number) else {
return Err(RuleSyntaxError::ExpectedReference(self.curr_tkn.clone()))
};
let num = number.value.parse::<usize>().unwrap();
return Ok(Some(ParseItem::new(ParseElement::Structure(terms, mods.suprs.stress, mods.suprs.tone, Some(num)), Position::new(self.group, self.line, start_pos, end_pos))))
}
Ok(Some(ParseItem::new(ParseElement::Structure(terms, mods.suprs.stress, mods.suprs.tone, None), Position::new(self.group, self.line, start_pos, end_pos))))
}
fn get_struct(&mut self) -> Result<Option<ParseItem>, RuleSyntaxError> {
let start_pos = self.curr_tkn.position.start;
if !self.expect(TokenKind::LeftAngle) { return Ok(None) }
let mut terms = Vec::new();
while self.has_more_tokens() {
if self.expect(TokenKind::RightAngle) { break; }
if let Some(item) = self.get_syll_term()? {
terms.push(item);
continue;
}
return Err(RuleSyntaxError::ExpectedStructElem(self.curr_tkn.clone()))
}
if !self.expect(TokenKind::Colon) {
if self.expect(TokenKind::Equals) {
let Some(number) = self.eat_expect(TokenKind::Number) else {
return Err(RuleSyntaxError::ExpectedReference(self.curr_tkn.clone()))
};
let num = number.value.parse::<usize>().unwrap();
let end_pos = self.token_list[self.pos-1].position.end;
return Ok(Some(ParseItem::new(ParseElement::Structure(terms, None, None, Some(num)), Position::new(self.group, self.line, start_pos, end_pos))))
}
let end_pos = self.token_list[self.pos-1].position.end;
return Ok(Some(ParseItem::new(ParseElement::Structure(terms, None, None, None), Position::new(self.group, self.line, start_pos, end_pos))))
}
if !self.expect(TokenKind::LeftSquare) {
return Err(RuleSyntaxError::ExpectedMatrix(self.curr_tkn.clone()))
}
let mods = self.get_param_args(true)?;
let end_pos = self.token_list[self.pos-1].position.end;
if self.expect(TokenKind::Equals) {
let Some(number) = self.eat_expect(TokenKind::Number) else {
return Err(RuleSyntaxError::ExpectedReference(self.curr_tkn.clone()))
};
let num = number.value.parse::<usize>().unwrap();
return Ok(Some(ParseItem::new(ParseElement::Structure(terms, mods.suprs.stress, mods.suprs.tone, Some(num)), Position::new(self.group, self.line, start_pos, end_pos))))
}
Ok(Some(ParseItem::new(ParseElement::Structure(terms, mods.suprs.stress, mods.suprs.tone, None), Position::new(self.group, self.line, start_pos, end_pos))))
}
fn get_term(&mut self) -> Result<Option<ParseItem>, RuleSyntaxError> {
if let Some(x) = self.get_syll()? { return Ok(Some(x)) }
if let Some(x) = self.get_struct()? { return Ok(Some(x)) }
if let Some(x) = self.get_set()? { return Ok(Some(x)) }
if let Some(x) = self.get_seg()? { return Ok(Some(x)) }
if let Some(x) = self.get_ref()? { return Ok(Some(x)) }
if let Some(x) = self.get_opt()? { return Err(RuleSyntaxError::OptLocError(x.position)) }
Ok(None)
}
fn get_input_els(&mut self) -> Result<Vec<ParseItem>, RuleSyntaxError> {
let mut els = Vec::new();
loop {
if let Some(w_el) = self.eat_expect(TokenKind::WrappedEllipsis) {
els.push(ParseItem::new(ParseElement::OptEllipsis, w_el.position))
} else if let Some(el) = self.eat_expect(TokenKind::Ellipsis) {
els.push(ParseItem::new(ParseElement::Ellipsis, el.position));
} else if let Some(x_bound) = self.get_x_bound() {
els.push(x_bound);
self.contains_external_in_input = true;
} else if let Some(s_bound) = self.get_syll_bound() {
els.push(s_bound);
} else if let Some(trm) = self.get_term()? {
els.push(trm)
} else if let Some(w_bound) = self.get_word_bound() {
return Err(RuleSyntaxError::WordBoundLoc(w_bound.position))
} else {
break
}
}
Ok(els)
}
fn get_output_el(&mut self) -> Result<Option<ParseItem>, RuleSyntaxError> {
if let Some(x) = self.get_syll()? { return Ok(Some(x)) }
if let Some(x) = self.get_struct()? { return Ok(Some(x)) }
if let Some(x) = self.get_set()? { return Ok(Some(x)) }
if let Some(x) = self.get_seg()? { return Ok(Some(x)) }
if let Some(x) = self.get_ref()? { return Ok(Some(x)) }
if let Some(x) = self.get_syll_bound() { return Ok(Some(x)) }
if let Some(w_el) = self.eat_expect(TokenKind::WrappedEllipsis) {
return Ok(Some(ParseItem::new(ParseElement::OptEllipsis, w_el.position)))
}
if let Some(el) = self.eat_expect(TokenKind::Ellipsis) {
return Ok(Some(ParseItem::new(ParseElement::Ellipsis, el.position)))
}
Ok(None)
}
fn get_output_els(&mut self) -> Result<Vec<ParseItem>, RuleSyntaxError> {
let mut els = Vec::new();
while let Some(el) = self.get_output_el()? {
els.push(el);
}
Ok(els)
}
fn get_empty(&mut self) -> Option<ParseItem> {
if !self.peek_expect(TokenKind::Star) && !self.peek_expect(TokenKind::EmptySet) {
return None
}
let token = self.eat();
Some(ParseItem::new(ParseElement::EmptySet, token.position))
}
fn get_input(&mut self) -> Result<Vec<Vec<ParseItem>>, RuleSyntaxError> {
let mut inputs = Vec::new();
loop {
if let Some(empty) = self.get_empty() {
inputs.push(vec![empty]);
if !self.expect(TokenKind::Comma) && (!self.peek_expect(TokenKind::Arrow) && !self.peek_expect(TokenKind::GreaterThan)) && !self.peek_expect(TokenKind::Reverse) {
return Err(RuleSyntaxError::InsertErr(self.curr_tkn.clone()))
}
continue;
}
let inp_term = self.get_input_els()?;
if inp_term.is_empty() {
match self.curr_tkn.kind {
TokenKind::Comma if inputs.is_empty() => return Err(RuleSyntaxError::EmptyInput(self.group, self.line, self.pos)),
TokenKind::Diacritic(_) => return Err(RuleSyntaxError::FloatingDiacritic(self.curr_tkn.position)),
_ if inputs.is_empty() => return Err(RuleSyntaxError::UnknownCharacter(self.curr_tkn.value.chars().next().unwrap(), self.group, self.line, self.pos)),
_ => break
}
}
inputs.push(inp_term);
if !self.expect(TokenKind::Comma) {
break
}
}
if inputs.is_empty() {
return Err(RuleSyntaxError::EmptyInput(self.group, self.line, self.token_list[self.pos].position.start))
}
Ok(inputs)
}
fn get_output(&mut self) -> Result<Vec<Vec<ParseItem>>, RuleSyntaxError> {
let mut outputs = Vec::new();
loop {
if let Some(el) = self.eat_expect(TokenKind::Ampersand) {
outputs.push(vec![ParseItem::new(ParseElement::Metathesis, el.position)]);
if !self.expect(TokenKind::Comma) && (!self.peek_expect(TokenKind::Slash) && !self.peek_expect(TokenKind::Pipe) && !self.peek_expect(TokenKind::Eol)) && !self.peek_expect(TokenKind::Comment) {
return Err(RuleSyntaxError::MetathErr(self.curr_tkn.clone()))
}
continue;
}
if let Some(el) = self.eat_expect(TokenKind::AtSign) {
outputs.push(vec![ParseItem::new(ParseElement::MetaOrdered, el.position)]);
if !self.expect(TokenKind::Comma) && (!self.peek_expect(TokenKind::Slash) && !self.peek_expect(TokenKind::Pipe) && !self.peek_expect(TokenKind::Eol)) && !self.peek_expect(TokenKind::Comment) {
return Err(RuleSyntaxError::MetathErr(self.curr_tkn.clone()))
}
continue;
}
if let Some(empty) = self.get_empty() {
outputs.push(vec![empty]);
if !self.expect(TokenKind::Comma) && !self.peek_expect(TokenKind::Slash) && !self.peek_expect(TokenKind::Pipe) && !self.peek_expect(TokenKind::Eol) && !self.peek_expect(TokenKind::Comment) {
return Err(RuleSyntaxError::DeleteErr(self.curr_tkn.clone()))
}
continue;
}
let out_term = self.get_output_els()?;
if out_term.is_empty() {
match self.curr_tkn.kind {
TokenKind::Comma if outputs.is_empty() => return Err(RuleSyntaxError::EmptyOutput(self.group, self.line, self.pos)),
TokenKind::Diacritic(_) => return Err(RuleSyntaxError::FloatingDiacritic(self.curr_tkn.position)),
TokenKind::Eol | TokenKind::Comment | TokenKind::Pipe | TokenKind::Slash if outputs.is_empty() => {
return Err(RuleSyntaxError::EmptyOutput(self.group, self.line, self.pos))
},
_ if outputs.is_empty() => return Err(RuleSyntaxError::UnknownCharacter(self.curr_tkn.value.chars().next().unwrap(), self.group, self.line, self.pos)),
_ => break
}
}
outputs.push(out_term);
if !self.expect(TokenKind::Comma) {
break
}
}
if outputs.is_empty() {
return Err(RuleSyntaxError::EmptyOutput(self.group, self.line, self.token_list[self.pos].position.start))
}
Ok(outputs)
}
fn rule(&mut self) -> Result<Rule, RuleSyntaxError> {
let input = self.get_input()?;
let prov_rev = match self.curr_tkn.kind {
TokenKind::Reverse => true,
TokenKind::Arrow | TokenKind::GreaterThan => false,
_ => return Err(RuleSyntaxError::ExpectedArrow(self.curr_tkn.clone()))
};
self.advance();
let output = self.get_output()?;
if self.expect(TokenKind::Eol) || self.expect(TokenKind::Comment) {
return Ok(Rule::new(input, output, Vec::new(), Vec::new(), prov_rev, self.contains_external_in_input, false))
}
if let TokenKind::Slash | TokenKind::Pipe = self.curr_tkn.kind {} else {
return Err(RuleSyntaxError::ExpectedEndLine(self.curr_tkn.clone()))
}
let context = self.get_context_block()?;
let except = self.get_except_block()?;
if !self.expect(TokenKind::Eol) && !self.expect(TokenKind::Comment) {
return Err(RuleSyntaxError::ExpectedEndLine(self.curr_tkn.clone()))
}
Ok(Rule::new(input, output, context, except, prov_rev, self.contains_external_in_input, self.contains_external_in_env))
}
pub(crate) fn parse(&mut self) -> Result<Option<Rule>, RuleSyntaxError> {
if self.curr_tkn.kind == TokenKind::Eol || self.curr_tkn.kind == TokenKind::Comment {
Ok(None)
} else {
Ok(Some(self.rule()?))
}
}
}
#[cfg(test)]
mod parser_tests {
use super::*;
use crate::{rule::Lexer, CARDINALS_MAP};
fn setup(test_str: &str) -> Vec<Token> {
match Lexer::new(&String::from(test_str).chars().collect::<Vec<_>>(),0,0).get_line() {
Ok(r) => r,
Err(e) => {
println!("{}", e.to_string());
assert!(false);
unreachable!()
},
}
}
#[test]
fn test_floating_diacritic() {
let maybe_result = Parser:: new(setup("a, \"H > \"h"), 0, 0).parse();
assert!(maybe_result.is_err());
assert!(if let RuleSyntaxError::FloatingDiacritic(..) = maybe_result.unwrap_err() {true} else {false} );
}
#[test]
fn test_trailing_comma() {
let maybe_result = Parser:: new(setup("a, > e"), 0, 0).parse();
assert!(maybe_result.is_ok());
let result = maybe_result.unwrap().unwrap();
assert_eq!(result.input.len(), 1);
assert_eq!(result.output.len(), 1);
assert!(result.context.is_empty());
assert!(result.except.is_empty());
let maybe_result = Parser:: new(setup("a, b, > e"), 0, 0).parse();
assert!(maybe_result.is_ok());
let result = maybe_result.unwrap().unwrap();
assert_eq!(result.input.len(), 2);
assert_eq!(result.output.len(), 1);
assert!(result.context.is_empty());
assert!(result.except.is_empty());
}
#[test]
fn test_multi_rule() {
let maybe_result = Parser:: new(setup("%:[+stress], % > [-stress], [+stress] / _ , #_"), 0, 0).parse();
assert!(maybe_result.is_ok());
let result = maybe_result.unwrap().unwrap();
assert_eq!(result.input.len(), 2);
assert_eq!(result.output.len(), 2);
assert_eq!(result.context.len(), 2);
assert!(result.except.is_empty());
let exp_input = vec![
ParseItem::new(ParseElement::Syllable(Some(SpecMod::First(ModKind::Binary(BinMod::Positive))), None, None), Position::new(0, 0, 0, 11)),
ParseItem::new(ParseElement::Syllable(None, None, None), Position::new(0, 0, 13, 14)),
];
let mut x = Modifiers::new();
let mut y = Modifiers::new();
x.suprs.stress = Some(SpecMod::First(ModKind::Binary(BinMod::Negative)));
y.suprs.stress = Some(SpecMod::First(ModKind::Binary(BinMod::Positive)));
let exp_output = vec![
ParseItem::new(ParseElement::Matrix(x, None), Position::new(0, 0, 17, 26)),
ParseItem::new(ParseElement::Matrix(y, None), Position::new(0, 0, 28, 37)),
];
let exp_context: Vec<EnvItem> = vec![
EnvItem { envs: vec![Env { before: vec![], after: vec![], center:None, position: Position::new(0, 0, 40, 41)}], position: Position::new(0, 0, 40, 41) },
EnvItem { envs: vec![Env { before: vec![ParseItem::new(ParseElement::WordBound, Position::new(0, 0, 44, 45))], after: vec![], center:None, position: Position::new(0, 0, 44, 46)}], position: Position::new(0, 0, 44, 46) },
];
assert_eq!(result.input[0][0], exp_input[0], "1");
assert_eq!(result.input[1][0], exp_input[1], "2");
assert_eq!(result.output[0][0], exp_output[0], "3");
assert_eq!(result.output[1][0], exp_output[1], "4");
assert_eq!(result.context[0], exp_context[0], "5");
assert_eq!(result.context[1], exp_context[1], "6");
}
#[test]
fn test_metathesis() {
let maybe_result = Parser::new(setup("t͡ɕ...b͡β > &"), 0, 0).parse();
assert!(maybe_result.is_ok());
let result = maybe_result.unwrap().unwrap();
assert_eq!(result.input.len(), 1);
assert_eq!(result.output.len(), 1);
assert!(result.context.is_empty());
assert!(result.except.is_empty());
let exp_input_res = vec![
ParseItem::new(ParseElement::Ipa(CARDINALS_MAP.get("t͡ɕ").unwrap().clone(), None),Position::new(0, 0, 0, 3)),
ParseItem::new(ParseElement::Ellipsis, Position::new(0, 0, 3, 6)),
ParseItem::new(ParseElement::Ipa(CARDINALS_MAP.get("b͡β").unwrap().clone(), None), Position::new(0, 0, 6, 9)),
];
assert_eq!(result.input[0][0], exp_input_res[0]);
assert_eq!(result.input[0][1], exp_input_res[1]);
assert_eq!(result.input[0][2], exp_input_res[2]);
}
#[test]
fn test_references_plain() {
let mut x = Modifiers::new();
x.feats[FeatKind::Syllabic as usize] = Some(ModKind::Binary(BinMod::Negative));
let _c = ParseItem::new(ParseElement::Matrix(x, Some(1)), Position::new(0, 0, 0, 1));
let mut y = Modifiers::new();
y.feats[FeatKind::Consonantal as usize] = Some(ModKind::Binary(BinMod::Negative));
y.feats[FeatKind::Sonorant as usize] = Some(ModKind::Binary(BinMod::Positive));
y.feats[FeatKind::Syllabic as usize] = Some(ModKind::Binary(BinMod::Positive));
let _v = ParseItem::new(ParseElement::Matrix(y, Some(2)), Position::new(0, 0, 4, 5));
let maybe_result = Parser:: new(setup("C=1 V=2 > 2 1 / _C"), 0, 0).parse();
assert!(maybe_result.is_ok());
let result = maybe_result.unwrap().unwrap();
assert_eq!(result.input.len(), 1);
assert_eq!(result.output.len(), 1);
assert_eq!(result.context.len(), 1);
assert!(result.except.is_empty());
}
#[test]
fn test_tone() {
let maybe_result = Parser::new(setup("%:[tone: 123] > [tone: 321]"), 0, 0).parse();
assert!(maybe_result.is_ok());
let result = maybe_result.unwrap().unwrap();
let exp_input = ParseItem::new(ParseElement::Syllable(None, Some(123), None), Position::new(0, 0, 0, 13));
let mut out = Modifiers::new();
out.suprs.tone = Some(321);
let exp_output = ParseItem::new(ParseElement::Matrix(out, None), Position::new(0, 0, 16, 27));
assert_eq!(result.input[0][0], exp_input);
assert_eq!(result.output[0][0], exp_output);
}
#[test]
fn test_comments() {
let maybe_result = Parser::new(setup("%:[tone: 123] > [tone: 321] ;; hello"), 0, 0).parse();
assert!(maybe_result.is_ok());
let result = maybe_result.unwrap().unwrap();
let exp_input = ParseItem::new(ParseElement::Syllable(None, Some(123), None), Position::new(0, 0, 0, 13));
let mut out: Modifiers = Modifiers::new();
out.suprs.tone = Some(321);
let exp_output = ParseItem::new(ParseElement::Matrix(out, None), Position::new(0, 0, 16, 27));
assert_eq!(result.input[0][0], exp_input);
assert_eq!(result.output[0][0], exp_output);
let maybe_result = Parser::new(setup(";; %:[tone: 123] > [tone: 321]"), 0, 0).parse();
assert!(maybe_result.is_ok());
assert!(maybe_result.unwrap().is_none());
let maybe_result = Parser::new(setup("%:[tone: 123] > [tone: 321] | a_ ;; test"), 0, 0).parse();
assert!(maybe_result.is_ok());
let maybe_result = Parser:: new(setup("ə > * / _ ;; unstressed schwa deletes"), 0, 0).parse();
assert!(maybe_result.is_ok());
let maybe_result = Parser::new(setup("%;;:[tone: 123] > [tone: 321]"), 0, 0).parse();
assert!(maybe_result.is_err());
assert!(if let RuleSyntaxError::ExpectedArrow(_) = maybe_result.unwrap_err() {true} else {false} );
let maybe_result = Parser::new(setup("%:;;[tone: 123] > [tone: 321]"), 0, 0).parse();
assert!(maybe_result.is_err());
assert!(if let RuleSyntaxError::ExpectedMatrix(_) = maybe_result.unwrap_err() {true} else {false} );
let maybe_result = Parser::new(setup("%:[tone: 123] ;; > [tone: 321]"), 0, 0).parse();
assert!(maybe_result.is_err());
assert!(if let RuleSyntaxError::ExpectedArrow(_) = maybe_result.unwrap_err() {true} else {false} );
let maybe_result = Parser::new(setup("%:[tone: 123] > ;; [tone: 321]"), 0, 0).parse();
assert!(maybe_result.is_err());
assert!(if let RuleSyntaxError::EmptyOutput(..) = maybe_result.unwrap_err() {true} else {false} );
let maybe_result = Parser::new(setup("%:[tone: 123] > [;;tone: 321]"), 0, 0).parse();
assert!(maybe_result.is_err());
assert!(if let RuleSyntaxError::ExpectedTokenFeature(..) = maybe_result.unwrap_err() {true} else {false} );
let maybe_result = Parser::new(setup("%:[tone: 123] > [tone: 321;;]"), 0, 0).parse();
assert!(maybe_result.is_err());
assert!(if let RuleSyntaxError::ExpectedTokenFeature(..) = maybe_result.unwrap_err() {true} else {false} );
}
#[test]
fn test_spec_struct() {
let maybe_result = Parser::new(setup("a > e / <sn_sns>"), 0, 0).parse();
eprintln!("{:?}", maybe_result);
let result = maybe_result.unwrap().unwrap();
let x = crate::word::Word::new("sin").unwrap();
let exp_struct = UnderlineStruct {
before: vec![
ParseItem::new(ParseElement::Ipa(x.syllables[0].segments[2], None), Position::new(0, 0, 10, 11)),
ParseItem::new(ParseElement::Ipa(x.syllables[0].segments[0], None), Position::new(0, 0, 9, 10)),
],
after: vec![
ParseItem::new(ParseElement::Ipa(x.syllables[0].segments[0], None), Position::new(0, 0, 12, 13)),
ParseItem::new(ParseElement::Ipa(x.syllables[0].segments[2], None), Position::new(0, 0, 13, 14)),
ParseItem::new(ParseElement::Ipa(x.syllables[0].segments[0], None), Position::new(0, 0, 14, 15)),
],
stress: None,
tone: None,
position: Position::new(0, 0, 8, 16)
};
let exp_env = Env { before: vec![], after: vec![], center: Some(exp_struct), position: Position::new(0, 0, 8, 16) };
assert_eq!(result.context[0].envs, vec![exp_env]);
}
#[test]
fn test_spec_struct_bef() {
let maybe_result = Parser::new(setup("a > e / <s_n><sin>=1"), 0, 0).parse();
eprintln!("{:?}", maybe_result);
assert!(maybe_result.is_ok());
let maybe_result = Parser::new(setup("a > e / <sin>=1<s_n>"), 0, 0).parse();
eprintln!("{:?}", maybe_result);
assert!(maybe_result.is_ok());
let maybe_result = Parser::new(setup("a > e / <sin>=1<s_n>=1"), 0, 0).parse();
eprintln!("{:?}", maybe_result);
assert!(maybe_result.is_err());
let maybe_result = Parser::new(setup("a > e / <s_n>=1<sin>=1"), 0, 0).parse();
eprintln!("{:?}", maybe_result);
assert!(maybe_result.is_err());
let maybe_result = Parser::new(setup("a > e / <s_n>=1"), 0, 0).parse();
eprintln!("{:?}", maybe_result);
assert!(maybe_result.is_err());
}
#[test]
fn test_exceptions() {
let maybe_res = Parser::new(setup("a > e / _ // _u"), 0, 0).parse();
assert!(maybe_res.is_ok());
let result = maybe_res.unwrap().unwrap();
let itm = ParseItem::new(ParseElement::Ipa(CARDINALS_MAP.get("u").unwrap().clone(), None),Position::new(0, 0, 14, 15));
let exp_cont = EnvItem { envs: vec![Env { center:None, before: vec![], after: vec![], position: Position::new(0, 0, 8, 9)}], position: Position::new(0, 0, 8, 9) };
let exp_expt = EnvItem { envs: vec![Env { center:None, before: vec![], after: vec![itm], position: Position::new(0, 0, 13, 15)}], position: Position::new(0, 0, 13, 15) };
assert_eq!(result.context[0], exp_cont);
assert_eq!(result.except[0] , exp_expt);
let maybe_res = Parser::new(setup("a > e / _ | _u"), 0, 0).parse();
assert!(maybe_res.is_ok());
let result = maybe_res.unwrap().unwrap();
let itm = ParseItem::new(ParseElement::Ipa(CARDINALS_MAP.get("u").unwrap().clone(), None),Position::new(0, 0, 13, 14));
let exp_cont = EnvItem{ envs: vec![Env { center:None, before: vec![], after: vec![], position: Position::new(0, 0, 8, 9)}], position: Position::new(0, 0, 8, 9) };
let exp_expt = EnvItem{ envs: vec![Env { center:None, before: vec![], after: vec![itm], position: Position::new(0, 0, 12, 14)}], position: Position::new(0, 0, 12, 14) };
assert_eq!(result.context[0], exp_cont);
assert_eq!(result.except[0] , exp_expt);
let maybe_res = Parser::new(setup("a > e | _u"), 0, 0).parse();
assert!(maybe_res.is_ok());
let result = maybe_res.unwrap().unwrap();
let itm = ParseItem::new(ParseElement::Ipa(CARDINALS_MAP.get("u").unwrap().clone(), None),Position::new(0, 0, 9, 10));
let exp_expt = EnvItem { envs: vec![Env { center:None, before: vec![], after: vec![itm], position: Position::new(0, 0, 8, 10)}], position: Position::new(0, 0, 8, 10) };
assert_eq!(result.except[0] , exp_expt);
}
}