use crate::parse::keyword::Keyword;
use crate::parse::tokenize::Item;
use crate::{ParseErrorKind as EK, Result};
#[derive(Copy, Clone)]
enum ObjKind {
NoObj,
RequireObj,
ObjOk,
}
#[derive(Clone)]
pub(crate) struct TokenFmt<T: Keyword> {
kwd: T,
min_args: Option<usize>,
max_args: Option<usize>,
required: bool,
may_repeat: bool,
obj: ObjKind,
}
impl<T: Keyword> TokenFmt<T> {
pub(crate) fn kwd(&self) -> T {
self.kwd
}
fn item_matches_args<'a>(&self, item: &Item<'a, T>) -> Result<()> {
let n_args = item.n_args();
if let Some(max) = self.max_args {
if n_args > max {
return Err(EK::TooManyArguments
.with_msg(self.kwd.to_str())
.at_pos(item.pos()));
}
}
if let Some(min) = self.min_args {
if n_args < min {
return Err(EK::TooFewArguments
.with_msg(self.kwd.to_str())
.at_pos(item.pos()));
}
}
Ok(())
}
fn item_matches_obj<'a>(&self, item: &Item<'a, T>) -> Result<()> {
match (&self.obj, item.has_obj()) {
(ObjKind::NoObj, true) => Err(EK::UnexpectedObject
.with_msg(self.kwd.to_str())
.at_pos(item.pos())),
(ObjKind::RequireObj, false) => Err(EK::MissingObject
.with_msg(self.kwd.to_str())
.at_pos(item.pos())),
(_, _) => Ok(()),
}
}
pub(crate) fn check_item<'a>(&self, item: &Item<'a, T>) -> Result<()> {
self.item_matches_args(item)?;
self.item_matches_obj(item)
}
pub(crate) fn check_multiplicity<'a>(&self, items: &[Item<'a, T>]) -> Result<()> {
match items.len() {
0 => {
if self.required {
return Err(EK::MissingToken.with_msg(self.kwd.to_str()));
}
}
1 => (),
_ => {
if !self.may_repeat {
return Err(EK::DuplicateToken
.with_msg(self.kwd.to_str())
.at_pos(items[1].pos()));
}
}
}
Ok(())
}
}
pub struct TokenFmtBuilder<T: Keyword>(TokenFmt<T>);
impl<T: Keyword> From<TokenFmtBuilder<T>> for TokenFmt<T> {
fn from(builder: TokenFmtBuilder<T>) -> Self {
builder.0
}
}
impl<T: Keyword> TokenFmtBuilder<T> {
pub(crate) fn new(t: T) -> Self {
Self(TokenFmt {
kwd: t,
min_args: None,
max_args: None,
required: false,
may_repeat: false,
obj: ObjKind::NoObj,
})
}
pub(crate) fn required(self) -> Self {
Self(TokenFmt {
required: true,
..self.0
})
}
pub(crate) fn may_repeat(self) -> Self {
Self(TokenFmt {
may_repeat: true,
..self.0
})
}
pub(crate) fn no_args(self) -> Self {
Self(TokenFmt {
max_args: Some(0),
..self.0
})
}
pub(crate) fn args<R>(self, r: R) -> Self
where
R: std::ops::RangeBounds<usize>,
{
use std::ops::Bound::*;
let min_args = match r.start_bound() {
Included(x) => Some(*x),
Excluded(x) => Some(*x + 1),
Unbounded => None,
};
let max_args = match r.end_bound() {
Included(x) => Some(*x),
Excluded(x) => Some(*x - 1),
Unbounded => None,
};
Self(TokenFmt {
min_args,
max_args,
..self.0
})
}
pub(crate) fn obj_optional(self) -> Self {
Self(TokenFmt {
obj: ObjKind::ObjOk,
..self.0
})
}
pub(crate) fn obj_required(self) -> Self {
Self(TokenFmt {
obj: ObjKind::RequireObj,
..self.0
})
}
}