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