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::{ParseErrorKind as EK, Result};
6

            
7
/// May an Item take an object?
8
385
#[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
385
#[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.
39
10000
    pub(crate) fn kwd(&self) -> T {
40
10000
        self.kwd
41
10000
    }
42
    /// Check whether a single Item matches this TokenFmt rule, with respect
43
    /// to its number of arguments.
44
2126
    fn item_matches_args<'a>(&self, item: &Item<'a, T>) -> Result<()> {
45
2126
        let n_args = item.n_args();
46
2126
        if let Some(max) = self.max_args {
47
509
            if n_args > max {
48
1
                return Err(EK::TooManyArguments
49
1
                    .with_msg(self.kwd.to_str())
50
1
                    .at_pos(item.pos()));
51
508
            }
52
1617
        }
53
2125
        if let Some(min) = self.min_args {
54
1000
            if n_args < min {
55
1
                return Err(EK::TooFewArguments
56
1
                    .with_msg(self.kwd.to_str())
57
1
                    .at_pos(item.pos()));
58
999
            }
59
1125
        }
60
2124
        Ok(())
61
2126
    }
62

            
63
    /// Check whether a single Item matches a TokenFmt rule, with respect
64
    /// to its object's presence and type.
65
2124
    fn item_matches_obj<'a>(&self, item: &Item<'a, T>) -> Result<()> {
66
2124
        match (&self.obj, item.has_obj()) {
67
            (ObjKind::NoObj, true) => Err(EK::UnexpectedObject
68
                .with_msg(self.kwd.to_str())
69
                .at_pos(item.pos())),
70
1
            (ObjKind::RequireObj, false) => Err(EK::MissingObject
71
1
                .with_msg(self.kwd.to_str())
72
1
                .at_pos(item.pos())),
73
2123
            (_, _) => Ok(()),
74
        }
75
2124
    }
76

            
77
    /// Check whether a single item has the right number of arguments
78
    /// and object.
79
    pub(crate) fn check_item<'a>(&self, item: &Item<'a, T>) -> Result<()> {
80
2126
        self.item_matches_args(item)?;
81
2124
        self.item_matches_obj(item)
82
2126
    }
83

            
84
    /// Check whether this kind of item may appear this many times.
85
5146
    pub(crate) fn check_multiplicity<'a>(&self, items: &[Item<'a, T>]) -> Result<()> {
86
5146
        match items.len() {
87
            0 => {
88
889
                if self.required {
89
3
                    return Err(EK::MissingToken.with_msg(self.kwd.to_str()));
90
886
                }
91
            }
92
4253
            1 => (),
93
            _ => {
94
4
                if !self.may_repeat {
95
1
                    return Err(EK::DuplicateToken
96
1
                        .with_msg(self.kwd.to_str())
97
1
                        .at_pos(items[1].pos()));
98
3
                }
99
            }
100
        }
101
5142
        Ok(())
102
5146
    }
103
}
104

            
105
/// Represents a TokenFmt under construction.
106
///
107
/// To construct a rule, create this type with Keyword::rule(), then
108
/// call method on it to set its fields, and then pass it to
109
/// SectionRules::add().
110
///
111
/// # Example
112
///
113
/// ```ignore
114
/// // There must be exactly one "ROUTER" entry, with 5 or more arguments.
115
/// section_rules.add(D.rule().required().args(5..));
116
/// ```
117
///
118
/// TODO: I'd rather have this be pub(crate), but I haven't figured out
119
/// how to make that work.  There are complicated cascading side-effects.
120
pub struct TokenFmtBuilder<T: Keyword>(TokenFmt<T>);
121

            
122
impl<T: Keyword> From<TokenFmtBuilder<T>> for TokenFmt<T> {
123
939
    fn from(builder: TokenFmtBuilder<T>) -> Self {
124
939
        builder.0
125
939
    }
126
}
127

            
128
impl<T: Keyword> TokenFmtBuilder<T> {
129
    /// Make a new TokenFmtBuilder with default behavior.
130
    ///
131
    /// (By default, all arguments are allowed, the Item may appear 0
132
    /// or 1 times, and it may not take an object.)
133
939
    pub(crate) fn new(t: T) -> Self {
134
939
        Self(TokenFmt {
135
939
            kwd: t,
136
939
            min_args: None,
137
939
            max_args: None,
138
939
            required: false,
139
939
            may_repeat: false,
140
939
            obj: ObjKind::NoObj,
141
939
        })
142
939
    }
143

            
144
    /// Indicate that this Item is required.
145
    ///
146
    /// By default, no item is required.
147
447
    pub(crate) fn required(self) -> Self {
148
447
        Self(TokenFmt {
149
447
            required: true,
150
447
            ..self.0
151
447
        })
152
447
    }
153
    /// Indicate that this Item is required.
154
    ///
155
    /// By default, items may not repeat.
156
132
    pub(crate) fn may_repeat(self) -> Self {
157
132
        Self(TokenFmt {
158
132
            may_repeat: true,
159
132
            ..self.0
160
132
        })
161
132
    }
162

            
163
    /// Indicate that this Item takes no arguments.
164
    ///
165
    /// By default, items may take any number of arguments.
166
115
    pub(crate) fn no_args(self) -> Self {
167
115
        Self(TokenFmt {
168
115
            max_args: Some(0),
169
115
            ..self.0
170
115
        })
171
115
    }
172
    /// Indicate that this item takes a certain number of arguments.
173
    ///
174
    /// The number of arguments is provided as a range, like `5..`.
175
435
    pub(crate) fn args<R>(self, r: R) -> Self
176
435
    where
177
435
        R: std::ops::RangeBounds<usize>,
178
435
    {
179
        use std::ops::Bound::*;
180
435
        let min_args = match r.start_bound() {
181
435
            Included(x) => Some(*x),
182
            Excluded(x) => Some(*x + 1),
183
            Unbounded => None,
184
        };
185
435
        let max_args = match r.end_bound() {
186
38
            Included(x) => Some(*x),
187
            Excluded(x) => Some(*x - 1),
188
397
            Unbounded => None,
189
        };
190
435
        Self(TokenFmt {
191
435
            min_args,
192
435
            max_args,
193
435
            ..self.0
194
435
        })
195
435
    }
196
    /// Indicate that this token takes an optional object.
197
    ///
198
    /// By default, objects are not allowed.
199
94
    pub(crate) fn obj_optional(self) -> Self {
200
94
        Self(TokenFmt {
201
94
            obj: ObjKind::ObjOk,
202
94
            ..self.0
203
94
        })
204
94
    }
205
    /// Indicate that this token takes an required object.
206
    ///
207
    /// By default, objects are not allowed.
208
97
    pub(crate) fn obj_required(self) -> Self {
209
97
        Self(TokenFmt {
210
97
            obj: ObjKind::RequireObj,
211
97
            ..self.0
212
97
        })
213
97
    }
214
}