1 //! Keywords for interpreting items and rules for validating them. 2 3 use crate::parse::keyword::Keyword; 4 use crate::parse::tokenize::Item; 5 use crate::{Error, Result}; 6 7 /// May an Item take an object? 8 #[derive(Copy, Clone)] 9 enum ObjKind { 10 /// No object is allowed. 11 NoObj, 12 /// An object is required. 13 RequireObj, 14 /// An object is optional. 15 ObjOk, 16 } 17 18 /// A set of restrictions to place on Items for a single keyword. 19 /// 20 /// These are built by the TokenFmtBuilder API. 21 #[derive(Clone)] 22 pub(crate) struct TokenFmt<T: Keyword> { 23 /// Which keyword is being restricted? 24 kwd: T, 25 /// If present, a lower bound on how many arguments may be present. 26 min_args: Option<usize>, 27 /// If present, an upper bound on how many arguments may be present. 28 max_args: Option<usize>, 29 /// If true, then at least one of this Item must appear. 30 required: bool, 31 /// If false, then no more than one this Item may appear. 32 may_repeat: bool, 33 /// May this Item have an object? Must it? 34 obj: ObjKind, 35 } 36 37 impl<T: Keyword> TokenFmt<T> { 38 /// Return the keyword that this rule restricts. kwd(&self) -> T39 pub(crate) fn kwd(&self) -> T { 40 self.kwd 41 } 42 /// Check whether a single Item matches this TokenFmt rule, with respect 43 /// to its number of arguments. item_matches_args<'a>(&self, item: &Item<'a, T>) -> Result<()>44 fn item_matches_args<'a>(&self, item: &Item<'a, T>) -> Result<()> { 45 let n_args = item.n_args(); 46 if let Some(max) = self.max_args { 47 if n_args > max { 48 return Err(Error::TooManyArguments(self.kwd.to_str(), item.pos())); 49 } 50 } 51 if let Some(min) = self.min_args { 52 if n_args < min { 53 return Err(Error::TooFewArguments(self.kwd.to_str(), item.pos())); 54 } 55 } 56 Ok(()) 57 } 58 59 /// Check whether a single Item matches a TokenFmt rule, with respect 60 /// to its object's presence and type. item_matches_obj<'a>(&self, item: &Item<'a, T>) -> Result<()>61 fn item_matches_obj<'a>(&self, item: &Item<'a, T>) -> Result<()> { 62 match (&self.obj, item.has_obj()) { 63 (ObjKind::NoObj, true) => Err(Error::UnexpectedObject(self.kwd.to_str(), item.pos())), 64 (ObjKind::RequireObj, false) => { 65 Err(Error::MissingObject(self.kwd.to_str(), item.pos())) 66 } 67 (_, _) => Ok(()), 68 } 69 } 70 71 /// Check whether a single item has the right number of arguments 72 /// and object. check_item<'a>(&self, item: &Item<'a, T>) -> Result<()>73 pub(crate) fn check_item<'a>(&self, item: &Item<'a, T>) -> Result<()> { 74 self.item_matches_args(item)?; 75 self.item_matches_obj(item) 76 } 77 78 /// Check whether this kind of item may appear this many times. check_multiplicity<'a>(&self, items: &[Item<'a, T>]) -> Result<()>79 pub(crate) fn check_multiplicity<'a>(&self, items: &[Item<'a, T>]) -> Result<()> { 80 match items.len() { 81 0 => { 82 if self.required { 83 return Err(Error::MissingToken(self.kwd.to_str())); 84 } 85 } 86 1 => (), 87 _ => { 88 if !self.may_repeat { 89 return Err(Error::DuplicateToken(self.kwd.to_str(), items[1].pos())); 90 } 91 } 92 } 93 Ok(()) 94 } 95 } 96 97 /// Represents a TokenFmt under construction. 98 /// 99 /// To construct a rule, create this type with Keyword::rule(), then 100 /// call method on it to set its fields, and then pass it to 101 /// SectionRules::add(). 102 /// 103 /// # Example 104 /// 105 /// ```ignore 106 /// // There must be exactly one "ROUTER" entry, with 5 or more arguments. 107 /// section_rules.add(D.rule().required().args(5..)); 108 /// ``` 109 /// 110 /// TODO: I'd rather have this be pub(crate), but I haven't figured out 111 /// how to make that work. There are complicated cascading side-effects. 112 pub struct TokenFmtBuilder<T: Keyword>(TokenFmt<T>); 113 114 impl<T: Keyword> From<TokenFmtBuilder<T>> for TokenFmt<T> { from(builder: TokenFmtBuilder<T>) -> Self115 fn from(builder: TokenFmtBuilder<T>) -> Self { 116 builder.0 117 } 118 } 119 120 impl<T: Keyword> TokenFmtBuilder<T> { 121 /// Make a new TokenFmtBuilder with default behavior. 122 /// 123 /// (By default, all arguments are allowed, the Item may appear 0 124 /// or 1 times, and it may not take an object.) new(t: T) -> Self125 pub(crate) fn new(t: T) -> Self { 126 Self(TokenFmt { 127 kwd: t, 128 min_args: None, 129 max_args: None, 130 required: false, 131 may_repeat: false, 132 obj: ObjKind::NoObj, 133 }) 134 } 135 136 /// Indicate that this Item is required. 137 /// 138 /// By default, no item is required. required(self) -> Self139 pub(crate) fn required(self) -> Self { 140 Self(TokenFmt { 141 required: true, 142 ..self.0 143 }) 144 } 145 /// Indicate that this Item is required. 146 /// 147 /// By default, items may not repeat. may_repeat(self) -> Self148 pub(crate) fn may_repeat(self) -> Self { 149 Self(TokenFmt { 150 may_repeat: true, 151 ..self.0 152 }) 153 } 154 155 /// Indicate that this Item takes no arguments. 156 /// 157 /// By default, items may take any number of arguments. no_args(self) -> Self158 pub(crate) fn no_args(self) -> Self { 159 Self(TokenFmt { 160 max_args: Some(0), 161 ..self.0 162 }) 163 } 164 /// Indicate that this item takes a certain number of arguments. 165 /// 166 /// The number of arguments is provided as a range, like `5..`. args<R>(self, r: R) -> Self where R: std::ops::RangeBounds<usize>,167 pub(crate) fn args<R>(self, r: R) -> Self 168 where 169 R: std::ops::RangeBounds<usize>, 170 { 171 use std::ops::Bound::*; 172 let min_args = match r.start_bound() { 173 Included(x) => Some(*x), 174 Excluded(x) => Some(*x + 1), 175 Unbounded => None, 176 }; 177 let max_args = match r.end_bound() { 178 Included(x) => Some(*x), 179 Excluded(x) => Some(*x - 1), 180 Unbounded => None, 181 }; 182 Self(TokenFmt { 183 min_args, 184 max_args, 185 ..self.0 186 }) 187 } 188 /// Indicate that this token takes an optional object. 189 /// 190 /// By default, objects are not allowed. obj_optional(self) -> Self191 pub(crate) fn obj_optional(self) -> Self { 192 Self(TokenFmt { 193 obj: ObjKind::ObjOk, 194 ..self.0 195 }) 196 } 197 /// Indicate that this token takes an required object. 198 /// 199 /// By default, objects are not allowed. obj_required(self) -> Self200 pub(crate) fn obj_required(self) -> Self { 201 Self(TokenFmt { 202 obj: ObjKind::RequireObj, 203 ..self.0 204 }) 205 } 206 } 207