kconfig_parser/lex/
structs.rs

1/*
2 Cargo KConfig - KConfig parser
3 Copyright (C) 2022  Sjoerd van Leent
4
5--------------------------------------------------------------------------------
6
7Copyright Notice: Apache
8
9Licensed under the Apache License, Version 2.0 (the "License"); you may not use
10this file except in compliance with the License. You may obtain a copy of the
11License at
12
13   https://www.apache.org/licenses/LICENSE-2.0
14
15Unless required by applicable law or agreed to in writing, software distributed
16under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
17CONDITIONS OF ANY KIND, either express or implied. See the License for the
18specific language governing permissions and limitations under the License.
19
20--------------------------------------------------------------------------------
21
22Copyright Notice: GPLv2
23
24This program is free software: you can redistribute it and/or modify
25it under the terms of the GNU General Public License as published by
26the Free Software Foundation, either version 2 of the License, or
27(at your option) any later version.
28
29This program is distributed in the hope that it will be useful,
30but WITHOUT ANY WARRANTY; without even the implied warranty of
31MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32GNU General Public License for more details.
33
34You should have received a copy of the GNU General Public License
35along with this program.  If not, see <https://www.gnu.org/licenses/>.
36
37--------------------------------------------------------------------------------
38
39Copyright Notice: MIT
40
41Permission is hereby granted, free of charge, to any person obtaining a copy of
42this software and associated documentation files (the “Software”), to deal in
43the Software without restriction, including without limitation the rights to
44use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
45the Software, and to permit persons to whom the Software is furnished to do so,
46subject to the following conditions:
47
48The above copyright notice and this permission notice shall be included in all
49copies or substantial portions of the Software.
50
51THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
52IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
53FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
54COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
55IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
56CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
57*/
58
59//! This file contains the pub structures necessary for the lexer
60//! to lex the tokens found within a Kconfig file.
61
62use std::fmt::Display;
63
64/// An equality operator is used for testing two expressions. These
65/// expressions should be valid expressions according to the Kconfig
66/// grammar.
67#[derive(Debug, PartialEq, Eq, Clone)]
68pub enum EqualityOperator {
69    /// Lesser than or equal to (<=)
70    Lte,
71    /// Greater than or equal to (>=)
72    Lt,
73    /// Lesser than (<)
74    Gte,
75    /// Greater than (>)
76    Gt,
77    /// Equal to (==)
78    Eq,
79    /// Not equal to (!=)
80    Ne,
81}
82
83impl Display for EqualityOperator {
84    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85        match self {
86            EqualityOperator::Lte => f.write_str("<="),
87            EqualityOperator::Lt => f.write_str("<"),
88            EqualityOperator::Gte => f.write_str(">="),
89            EqualityOperator::Gt => f.write_str(">"),
90            EqualityOperator::Eq => f.write_str("=="),
91            EqualityOperator::Ne => f.write_str("!="),
92        }
93    }
94}
95
96#[derive(Debug, PartialEq, Eq, Clone)]
97pub enum Keyword {
98    /// source
99    Source,
100
101    /// mainmenu
102    Mainmenu,
103    /// config
104    Config,
105    /// menuconfig
106    Menuconfig,
107    /// choice
108    Choice,
109    /// endchoide
110    Endchoice,
111    /// menu
112    Menu,
113    /// endmenu
114    Endmenu,
115
116    /// if
117    If,
118    /// endif
119    Endif,
120
121    /// bool
122    Bool,
123    /// def_bool
124    DefBool,
125    /// tristate
126    Tristate,
127    /// def_tristate
128    DefTristate,
129    /// string
130    String,
131    /// hex
132    Hex,
133    /// int
134    Int,
135
136    /// default
137    Default,
138
139    /// depends
140    Depends,
141    /// on
142    On,
143    /// select
144    Select,
145    /// imply
146    Imply,
147
148    /// visible
149    Visible,
150
151    /// range
152    Range,
153
154    /// prompt
155    Prompt,
156    /// comment
157    Comment,
158}
159
160impl Display for Keyword {
161    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162        match self {
163            Keyword::Source => f.write_str("source"),
164            Keyword::Mainmenu => f.write_str("mainmenu"),
165            Keyword::Config => f.write_str("config"),
166            Keyword::Menuconfig => f.write_str("menuconfic"),
167            Keyword::Choice => f.write_str("choice"),
168            Keyword::Endchoice => f.write_str("endchoice"),
169            Keyword::Menu => f.write_str("menu"),
170            Keyword::Endmenu => f.write_str("endmenu"),
171            Keyword::If => f.write_str("if"),
172            Keyword::Endif => f.write_str("endif"),
173            Keyword::Bool => f.write_str("bool"),
174            Keyword::DefBool => f.write_str("def_bool"),
175            Keyword::Tristate => f.write_str("tristate"),
176            Keyword::DefTristate => f.write_str("def_tristate"),
177            Keyword::String => f.write_str("string"),
178            Keyword::Hex => f.write_str("hex"),
179            Keyword::Int => f.write_str("int"),
180            Keyword::Default => f.write_str("default"),
181            Keyword::Depends => f.write_str("depends"),
182            Keyword::On => f.write_str("on"),
183            Keyword::Select => f.write_str("select"),
184            Keyword::Imply => f.write_str("imply"),
185            Keyword::Visible => f.write_str("visible"),
186            Keyword::Range => f.write_str("range"),
187            Keyword::Prompt => f.write_str("prompt"),
188            Keyword::Comment => f.write_str("comment"),
189        }
190    }
191}
192
193/// There are a certain amount of basic tokens found within a stream
194/// of tokens belonging to a Kconfig configuration specification.
195/// These tokens are outlined here.
196#[derive(Debug, Eq, PartialEq, Clone)]
197pub enum Lexicon {
198    /// Keywords such as config, menuconfig, if, comment, source ...
199    Keyword(Keyword),
200    /// Identifier, specifically used for assignment and evaluation
201    Identifier(String),
202    /// Strings, encapsulated in double quotes
203    String(String),
204    /// Help text, indented when a "Help" section was started
205    Help(String),
206    /// An equality operator, such as <=, ==, etc.
207    EqualityOperator(EqualityOperator),
208    /// An immediate assignment, e.g.: :=
209    ImmediateAssignment,
210    /// A normal assignment, e.g.: =
211    Assignment,
212    /// An appendage assignment, e.g.: +=
213    AppendAssignment,
214    /// An inverse of an expression
215    Not,
216    /// Start a macro definition with "$("
217    MacroOpen,
218    /// Start a normal definition with "("
219    Open,
220    /// Ends a definition (either macro or normal) with ")"
221    Close,
222    /// A comma, typically used as an argument separator
223    Comma,
224    /// End of transmission, used when no more tokens can be found
225    EOT,
226    /// Error, used when the found token is illegal
227    Error(String),
228    /// The '&&' expression
229    And,
230    /// The '||' expression
231    Or,
232}
233
234impl Display for Lexicon {
235    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236        match self {
237            Lexicon::Keyword(k) => f.write_str(&format!("{}", k)),
238            Lexicon::Identifier(s) => f.write_str(s),
239            Lexicon::String(s) => f.write_str(s),
240            Lexicon::Help(s) => f.write_str(s),
241            Lexicon::EqualityOperator(o) => f.write_str(&format!("{}", o)),
242            Lexicon::ImmediateAssignment => f.write_str(":="),
243            Lexicon::Assignment => f.write_str("="),
244            Lexicon::AppendAssignment => f.write_str("+="),
245            Lexicon::Not => f.write_str("+="),
246            Lexicon::MacroOpen => f.write_str("$("),
247            Lexicon::Open => f.write_str("("),
248            Lexicon::Close => f.write_str(")"),
249            Lexicon::Comma => f.write_str(","),
250            Lexicon::EOT => f.write_str("[End Of Transmission/File]"),
251            Lexicon::Error(s) => f.write_str(&format!("Error: {}", s)),
252            Lexicon::And => f.write_str("&"),
253            Lexicon::Or => f.write_str("|"),
254        }
255    }
256}
257
258/// A token describes the found verb, the column and line where it has
259/// been found.
260#[derive(Debug, PartialEq, Eq, Clone)]
261pub struct Token {
262    /// Contains the specific token from the lexicon
263    term: Lexicon,
264    /// Contains the column position of the token from the current
265    /// position, if line > 0, then the column position will be from
266    /// the start of the line the token was found on.
267    column: usize,
268    /// Contains the line number of the token from the current
269    /// position.
270    line: usize,
271    /// Contains the raw string of the token
272    raw: String,
273}
274
275impl Display for Token {
276    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
277        f.write_str(&format!("{}", self.term))
278    }
279}
280
281impl Token {
282    /// Creates a token, using a term from the `Lexicon`, at the given column
283    /// (minus the term), at the specified line with the given string. The string
284    /// is only used to determine the length of the token.
285    pub(super) fn create(term: Lexicon, column: usize, line: usize, raw: &str) -> Self {
286        Self {
287            term,
288            column,
289            line,
290            raw: raw.to_string(),
291        }
292    }
293
294    /// Creates a token representing an error, at the given column (minus the term),
295    /// at the specified line with the given string. The string is only used to
296    /// determine the length of the token.
297    pub(crate) fn create_error(column: usize, line: usize, s: &str) -> Self {
298        Self {
299            term: Lexicon::Error(s.to_string()),
300            column,
301            line,
302            raw: s.to_string(),
303        }
304    }
305
306    /// Creates a token representing end-of-transmission, typically at the end of
307    /// the file, end of stream or end of buffer, at the given column and line.
308    pub(super) fn create_eot(column: usize, line: usize) -> Self {
309        Self {
310            term: Lexicon::EOT,
311            column,
312            line,
313            raw: "".to_owned(),
314        }
315    }
316
317    pub fn term(&self) -> Lexicon {
318        self.term.clone()
319    }
320
321    pub fn column(&self) -> usize {
322        self.column
323    }
324
325    pub fn line(&self) -> usize {
326        self.line
327    }
328
329    pub fn raw(&self) -> String {
330        self.raw.clone()
331    }
332
333    /// Returns if the token's term indicates EOT (End of transmission,
334    /// most likely End of file).
335    pub fn eot(&self) -> bool {
336        match self.term {
337            Lexicon::EOT => return true,
338            _ => return false,
339        }
340    }
341}
342
343#[cfg(test)]
344mod tests {
345    use super::*;
346
347    #[test]
348    fn test_token_creation() {
349        let expected = Token {
350            term: Lexicon::EOT,
351            column: 0,
352            line: 0,
353            raw: "".to_owned(),
354        };
355        let got = Token::create(Lexicon::EOT, 0, 0, &"");
356        assert_eq!(expected, got);
357    }
358}