kconfig_parser/ast/
parser.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 parser of the Kconfig file, and creates the
60//! Abstract Syntax Tree for it.
61
62use std::{
63    collections::{HashMap, HashSet, VecDeque},
64    fmt::Display,
65    hash::Hash,
66};
67
68use super::dependency_parser::parse as parse_dependency;
69use super::expr_parser::parse as parse_expr;
70use super::structs::*;
71use crate::lex::{
72    structs::{Keyword, Lexicon, Token},
73    LexerBase,
74};
75use crate::parse_string;
76
77#[derive(Clone, Debug, Eq, PartialEq)]
78pub struct Error {
79    token: Token,
80    msg: String,
81}
82
83impl Display for Error {
84    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
85        write!(
86            f,
87            "{} at {}:{}, found: {}",
88            self.msg,
89            self.token.line(),
90            self.token.column(),
91            self.token,
92        )
93    }
94}
95
96impl Error {
97    pub(super) fn new(token: Token, msg: &str) -> Self {
98        Self {
99            token,
100            msg: msg.to_string(),
101        }
102    }
103}
104
105/// Determines which config element type to use, this can either be
106/// Config or Menuconfig.
107/// Contains the Ast for the entire stream of tokens, including sourced
108/// elements.
109#[derive(Clone, Debug, Eq, PartialEq, Default)]
110pub struct Ast {
111    main_menu: Option<String>,
112    elements: Vec<(String, Element)>,
113    comment: String,
114    conditions: Vec<Condition>,
115}
116
117/// Describes a configuration element, typically starting with the
118/// "config" keyword
119#[derive(Clone, Debug, Eq, PartialEq)]
120pub struct ConfigElement {
121    config: Config,
122}
123
124/// Describes a menu-configuration element, typically starting with the
125/// "menuconfig" keyword
126#[derive(Clone, Debug, Eq, PartialEq)]
127pub struct MenuConfigElement {
128    config: Config,
129}
130
131/// Describes a menu element, typically starting with the "menu" keyword
132#[derive(Clone, Debug, Eq, PartialEq)]
133pub struct Menu {
134    config: Config,
135    visible_if: HashSet<Dependency>,
136    elements: Vec<(String, Element)>,
137    comment: String,
138    conditions: Vec<Condition>,
139}
140
141/// Describes a condition, typically a statement part of another expression,
142/// which is introduced by appending "if" to the statement. This would apply
143/// a particular statement in a certain condition.
144#[derive(Clone, Debug, Eq, PartialEq)]
145pub struct Condition {
146    dependency: Dependency,
147    elements: Vec<(String, Element)>,
148    conditions: Vec<Condition>,
149}
150
151impl Condition {
152    pub fn new(dependency: &Dependency) -> Self {
153        Self {
154            dependency: dependency.clone(),
155            elements: Vec::new(),
156            conditions: Vec::new(),
157        }
158    }
159
160    pub fn dependency(&self) -> Dependency {
161        self.dependency.clone()
162    }
163
164    pub fn elements(&self) -> Vec<(String, Element)> {
165        self.elements.clone()
166    }
167}
168
169pub trait WithComment {
170    fn comment(&self) -> String;
171}
172
173impl WithComment for Ast {
174    fn comment(&self) -> String {
175        self.comment.clone()
176    }
177}
178
179impl WithComment for Menu {
180    fn comment(&self) -> String {
181        self.comment.clone()
182    }
183}
184
185trait Commentable {
186    fn set_comment(&mut self, s: &str);
187}
188
189impl Commentable for Ast {
190    fn set_comment(&mut self, s: &str) {
191        self.comment = s.to_string()
192    }
193}
194
195impl Commentable for Menu {
196    fn set_comment(&mut self, s: &str) {
197        self.comment = s.to_string()
198    }
199}
200
201pub trait ConditionContainer {
202    fn len_conditional_blocks(&self) -> usize;
203    fn conditional_blocks(&self) -> Vec<Condition>;
204}
205
206impl ConditionContainer for Ast {
207    fn len_conditional_blocks(&self) -> usize {
208        self.conditions.len()
209    }
210    fn conditional_blocks(&self) -> Vec<Condition> {
211        self.conditions.clone()
212    }
213}
214
215impl ConditionContainer for Menu {
216    fn len_conditional_blocks(&self) -> usize {
217        self.conditions.len()
218    }
219
220    fn conditional_blocks(&self) -> Vec<Condition> {
221        self.conditions.clone()
222    }
223}
224
225impl ConditionContainer for Condition {
226    fn len_conditional_blocks(&self) -> usize {
227        self.conditions.len()
228    }
229
230    fn conditional_blocks(&self) -> Vec<Condition> {
231        self.conditions.clone()
232    }
233}
234
235pub trait Container {
236    fn element_names(&self) -> Box<dyn Iterator<Item = String>>;
237}
238
239trait MutableContainer: Container {
240    fn insert_child(&mut self, name: &str, e: Element);
241    fn insert_condition(&mut self, e: Condition);
242    fn len(&self) -> usize;
243}
244
245impl Container for Ast {
246    fn element_names(&self) -> Box<dyn Iterator<Item = String>> {
247        Box::new(
248            self.elements
249                .clone()
250                .into_iter()
251                .map(|(name, _)| name.clone()),
252        )
253    }
254}
255
256impl Container for Menu {
257    fn element_names(&self) -> Box<dyn Iterator<Item = String>> {
258        Box::new(
259            self.elements
260                .clone()
261                .into_iter()
262                .map(|(name, _)| name.clone()),
263        )
264    }
265}
266
267impl Container for Condition {
268    fn element_names(&self) -> Box<dyn Iterator<Item = String>> {
269        Box::new(
270            self.elements
271                .clone()
272                .into_iter()
273                .map(|(name, _)| name.clone()),
274        )
275    }
276}
277
278impl MutableContainer for Ast {
279    fn insert_child(&mut self, name: &str, e: Element) {
280        self.elements.push((name.to_string(), e));
281    }
282
283    fn insert_condition(&mut self, e: Condition) {
284        self.conditions.push(e);
285    }
286
287    fn len(&self) -> usize {
288        self.elements.len()
289    }
290}
291
292impl MutableContainer for Menu {
293    fn insert_child(&mut self, name: &str, e: Element) {
294        self.elements.push((name.to_string(), e));
295    }
296
297    fn insert_condition(&mut self, e: Condition) {
298        self.conditions.push(e);
299    }
300
301    fn len(&self) -> usize {
302        self.elements.len()
303    }
304}
305
306impl MutableContainer for Condition {
307    fn insert_child(&mut self, name: &str, e: Element) {
308        self.elements.push((name.to_string(), e));
309    }
310
311    fn insert_condition(&mut self, e: Condition) {
312        self.conditions.push(e);
313    }
314
315    fn len(&self) -> usize {
316        self.elements.len()
317    }
318}
319
320trait BaseConfigElement {
321    fn create(name: &str) -> Self;
322    fn config(&mut self) -> &mut Config;
323    fn insert_into_hierarchy<I>(&mut self, hierarchy: &mut I)
324    where
325        I: MutableContainer;
326}
327
328impl BaseConfigElement for ConfigElement {
329    fn create(name: &str) -> Self {
330        let config = Config::create(name);
331        Self { config }
332    }
333
334    fn config(&mut self) -> &mut Config {
335        &mut self.config
336    }
337
338    fn insert_into_hierarchy<I>(&mut self, hierarchy: &mut I)
339    where
340        I: MutableContainer,
341    {
342        hierarchy.insert_child(&self.config().name.clone(), Element::Config(self.clone()));
343    }
344}
345
346impl BaseConfigElement for MenuConfigElement {
347    fn create(name: &str) -> Self {
348        let config = Config::create(name);
349        Self { config }
350    }
351
352    fn config(&mut self) -> &mut Config {
353        &mut self.config
354    }
355
356    fn insert_into_hierarchy<I>(&mut self, hierarchy: &mut I)
357    where
358        I: MutableContainer,
359    {
360        hierarchy.insert_child(
361            &self.config().name.clone(),
362            Element::MenuConfig(self.clone()),
363        );
364    }
365}
366
367impl BaseConfigElement for Menu {
368    fn create(name: &str) -> Self {
369        let config = Config::create(name);
370        Self {
371            config,
372            visible_if: HashSet::new(),
373            elements: Vec::new(),
374            comment: String::new(),
375            conditions: Vec::new(),
376        }
377    }
378
379    fn config(&mut self) -> &mut Config {
380        &mut self.config
381    }
382
383    fn insert_into_hierarchy<I>(&mut self, hierarchy: &mut I)
384    where
385        I: MutableContainer,
386    {
387        hierarchy.insert_child(&self.config().name.clone(), Element::Menu(self.clone()));
388    }
389}
390
391impl Hash for ConfigElement {
392    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
393        self.config.name.hash(state);
394    }
395}
396
397impl Hash for MenuConfigElement {
398    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
399        self.config.name.hash(state);
400    }
401}
402
403impl Hash for Menu {
404    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
405        self.config.name.hash(state);
406    }
407}
408
409impl Hash for Condition {
410    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
411        self.dependency.hash(state);
412    }
413}
414
415#[derive(Hash, Clone, Debug, Eq, PartialEq)]
416pub enum ElementType {
417    Config,
418    MenuConfig,
419    Menu,
420}
421
422/// An Ast contains a list of elements, which might themselves contain
423/// other parts of the Ast, etc.
424#[derive(Hash, Clone, Debug, Eq, PartialEq)]
425pub enum Element {
426    Config(ConfigElement),
427    MenuConfig(MenuConfigElement),
428    Menu(Menu),
429}
430
431impl Element {
432    pub fn len_conditional_blocks(&self) -> usize {
433        match self {
434            Element::Menu(elt) => elt.len_conditional_blocks(),
435            _ => 0,
436        }
437    }
438
439    pub fn conditional_blocks(&self) -> Vec<Condition> {
440        match self {
441            Element::Menu(elt) => elt.conditional_blocks(),
442            _ => Vec::new(),
443        }
444    }
445
446    pub fn dependencies(&self) -> HashSet<Dependency> {
447        match self {
448            Element::Config(elt) => elt.config.dependencies.clone(),
449            Element::MenuConfig(elt) => elt.config.dependencies.clone(),
450            Element::Menu(elt) => elt.config.dependencies.clone(),
451        }
452    }
453
454    pub fn name(&self) -> String {
455        match self {
456            Element::Config(elt) => elt.config.name.clone(),
457            Element::MenuConfig(elt) => elt.config.name.clone(),
458            Element::Menu(elt) => elt.config.name.clone(),
459        }
460    }
461
462    pub fn prompt(&self) -> Option<String> {
463        match self {
464            Element::Config(elt) => elt.config.prompt.as_ref().map(|s| s.to_string()),
465            Element::MenuConfig(elt) => elt.config.prompt.as_ref().map(|s| s.to_string()),
466            _ => None,
467        }
468    }
469
470    pub fn elt_type(&self) -> ElementType {
471        match self {
472            Element::Config(_) => ElementType::Config,
473            Element::MenuConfig(_) => ElementType::MenuConfig,
474            Element::Menu(_) => ElementType::Menu,
475        }
476    }
477
478    pub fn types(&self) -> Option<&HashMap<ConfigType, Option<Dependency>>> {
479        match self {
480            Element::Config(elt) => Some(&elt.config.types),
481            Element::MenuConfig(elt) => Some(&elt.config.types),
482            _ => None,
483        }
484    }
485
486    pub fn defaults(&self) -> Option<&HashMap<Expr, Option<Dependency>>> {
487        match self {
488            Element::Config(elt) => Some(&elt.config.defaults),
489            Element::MenuConfig(elt) => Some(&elt.config.defaults),
490            _ => None,
491        }
492    }
493
494    pub fn reverse_dependencies(&self) -> Option<&HashMap<String, Option<Dependency>>> {
495        match self {
496            Element::Config(elt) => Some(&elt.config.reverse_dependencies),
497            Element::MenuConfig(elt) => Some(&elt.config.reverse_dependencies),
498            Element::Menu(elt) => Some(&elt.config.reverse_dependencies),
499        }
500    }
501
502    pub fn weak_dependencies(&self) -> Option<&HashMap<String, Option<Dependency>>> {
503        match self {
504            Element::Config(elt) => Some(&elt.config.weak_dependencies),
505            Element::MenuConfig(elt) => Some(&elt.config.weak_dependencies),
506            Element::Menu(elt) => Some(&elt.config.weak_dependencies),
507        }
508    }
509
510    pub fn range(&self) -> Option<Range> {
511        match self {
512            Element::Config(elt) => elt.config.range.clone(),
513            Element::MenuConfig(elt) => elt.config.range.clone(),
514            _ => None,
515        }
516    }
517
518    pub fn help(&self) -> VecDeque<String> {
519        match self {
520            Element::Config(elt) => elt.config.help.clone(),
521            Element::MenuConfig(elt) => elt.config.help.clone(),
522            Element::Menu(elt) => elt.config.help.clone(),
523        }
524    }
525
526    pub fn visible_if(&self) -> HashSet<Dependency> {
527        match self {
528            Element::Menu(elt) => elt.visible_if.clone(),
529            _ => HashSet::new(),
530        }
531    }
532
533    pub fn sub_element(&self, s: &str) -> Option<&Element> {
534        match self {
535            // TODO, evaluate if this is a condition, and then continue
536            // to a subelement, and so forth.
537            Element::Menu(elt) => {
538                for (subname, subelt) in &elt.elements {
539                    if subname == s {
540                        return Some(subelt);
541                    };
542                }
543                None
544            }
545            _ => None,
546        }
547    }
548
549    pub fn sub_elements(&self) -> Vec<(String, Element)> {
550        match self {
551            // TODO, evaluate if this is a condition, and then continue
552            // to a subelement, and so forth.
553            Element::Menu(elt) => elt.elements.clone(),
554            _ => Vec::new(),
555        }
556    }
557
558    pub fn len(&self) -> usize {
559        match self {
560            // TODO, evaluate if this is a condition, and then continue
561            // to a subelement, and so forth.
562            Element::Menu(elt) => elt.len(),
563            _ => 0,
564        }
565    }
566}
567
568/// Checks if the given token has an error indication, if so, the
569/// result will be filled with the error, Otherwise an empty Ok
570/// will result
571fn has_error(t: &Token) -> Result<(), Error> {
572    if let Lexicon::Error(e) = t.term() {
573        let msg = format!("Erroneous element: {}", e);
574        Err(Error::new(t.clone(), &msg))
575    } else {
576        Ok(())
577    }
578}
579
580/// Will result into an Err element with the expected type_name
581/// in the message.
582fn result_unexpected(t: &Token, type_name: &str) -> Error {
583    let msg = format!("Expected {}", type_name);
584    Error::new(t.clone(), &msg)
585}
586
587/// Verify if the current token is a String, if not so, returns an error,
588/// otherwise returns the contents of the string.
589fn should_be_string(t: &Token) -> Result<String, Error> {
590    has_error(t)?;
591    match t.term() {
592        Lexicon::String(s) => Ok(s),
593        _ => Err(result_unexpected(t, "Prompt")),
594    }
595}
596
597/// Parses a prompt from the lexer after the Prompt keyword
598fn parse_prompt<LB, F>(lexer: &mut LB, mut closure: F) -> Result<Token, Error>
599where
600    LB: LexerBase,
601    F: FnMut(&str),
602{
603    let next_token = lexer.next_token();
604    match next_token.term() {
605        Lexicon::String(s) => match parse_string(&s) {
606            Ok(v) => closure(&v),
607            Err(e) => return Err(Error::new(next_token.clone(), &e)),
608        },
609        _ => return Err(Error::new(next_token.clone(), "Expected string")),
610    };
611    Ok(lexer.next_token())
612}
613
614/// Parses a bool type from the lexer after the Bool keyword
615fn parse_type<LB, Fp, Fd>(
616    lexer: &mut LB,
617    token: Token,
618    mut prompt_closure: Fp,
619    mut dependency_closure: Fd,
620) -> Result<Token, Error>
621where
622    LB: LexerBase,
623    Fp: FnMut(&str),
624    Fd: FnMut(&Dependency),
625{
626    let mut next_token = lexer.next_token();
627    let mut line = token.line();
628    if let Lexicon::String(s) = next_token.term() {
629        match parse_string(&s) {
630            Ok(v) => prompt_closure(&v),
631            Err(e) => return Err(Error::new(next_token.clone(), &e)),
632        }
633        line = next_token.line();
634        next_token = lexer.next_token();
635    };
636    if let Lexicon::Keyword(Keyword::If) = next_token.term() {
637        // The line of the if statement must be on the same line as the line
638        // of the prompt or the type indication
639        if line == next_token.line() {
640            let (dependency, result_token) = parse_dependency(lexer)?;
641            next_token = result_token;
642            dependency_closure(&dependency);
643        }
644    };
645    Ok(next_token)
646}
647
648/// Parses a default value, which requires an expression and optionally a dependency expression can be
649/// given through the if statement.
650fn parse_default<LB, F>(lexer: &mut LB, token: Token, mut closure: F) -> Result<Token, Error>
651where
652    LB: LexerBase,
653    F: FnMut(&Expr, Option<Dependency>),
654{
655    let line = token.line();
656    let (maybe_expr, mut next_token) = parse_expr(lexer)?;
657    let expr = match maybe_expr {
658        None => return Err(Error::new(token.clone(), "Expected expression")),
659        Some(expr) => expr,
660    };
661    let mut dep_result: Option<Dependency> = None;
662    if let Lexicon::Keyword(k) = next_token.term() {
663        if let Keyword::If = k {
664            if line == token.line() {
665                let (dep, new_token) = parse_dependency(lexer)?;
666
667                dep_result = Some(dep);
668                next_token = new_token;
669            }
670        }
671    }
672    closure(&expr, dep_result);
673    Ok(next_token)
674}
675
676/// Parses an auto default type, typically in the form of def_bool and def_tristate. The
677/// default value is given (as expression) and optionally a dependency expression can be
678/// given through the if statement.
679fn parse_def_type<LB, F>(lexer: &mut LB, token: Token, mut closure: F) -> Result<Token, Error>
680where
681    LB: LexerBase,
682    F: FnMut(&Expr, Option<Dependency>),
683{
684    let line = token.line();
685    let (maybe_expr, mut next_token) = parse_expr(lexer)?;
686    let expr = match maybe_expr {
687        None => return Err(Error::new(token.clone(), "Expected expression")),
688        Some(expr) => expr,
689    };
690    let mut dep_result: Option<Dependency> = None;
691    if let Lexicon::Keyword(k) = next_token.term() {
692        if let Keyword::If = k {
693            if line == token.line() {
694                let (dep, new_token) = parse_dependency(lexer)?;
695
696                dep_result = Some(dep);
697                next_token = new_token;
698            }
699        }
700    }
701    closure(&expr, dep_result);
702    Ok(next_token)
703}
704
705/// Parses a select statement which causes a reverse dependency. This is used by both
706/// reverse dependencies (select) and weak reverse dependencies (imply). A reverse
707/// dependencies forces a configuration, even though the regular dependencies should
708/// not allow it. A weak reverse dependency only sets the option if available and
709/// possible.
710fn parse_select<LB, F>(lexer: &mut LB, token: Token, mut closure: F) -> Result<Token, Error>
711where
712    LB: LexerBase,
713    F: FnMut(&str, Option<Dependency>),
714{
715    let line = token.line();
716    let mut next_token = lexer.next_token();
717    let sym = match next_token.term() {
718        Lexicon::Identifier(s) => {
719            next_token = lexer.next_token();
720            s
721        }
722        _ => return Err(Error::new(next_token.clone(), "Expected identifier")),
723    };
724
725    let mut dep_result: Option<Dependency> = None;
726    if let Lexicon::Keyword(k) = next_token.term() {
727        if let Keyword::If = k {
728            if line == token.line() {
729                let (dep, new_token) = parse_dependency(lexer)?;
730
731                dep_result = Some(dep);
732                next_token = new_token;
733            }
734        }
735    }
736    closure(&sym, dep_result);
737    Ok(next_token)
738}
739
740/// This function parses out a range (specifically for integer and hexadecimal usage). It
741/// requires two arguments to be set (lower limit and upper limit), optionally with a
742/// dependency expression
743fn parse_range<LB, F>(lexer: &mut LB, token: Token, mut closure: F) -> Result<Token, Error>
744where
745    LB: LexerBase,
746    F: FnMut(i64, i64, Option<Dependency>),
747{
748    let line = token.line();
749    let mut next_token = lexer.next_token();
750    let sym1 = match next_token.term() {
751        Lexicon::Identifier(s) => {
752            next_token = lexer.next_token();
753            s
754        }
755        _ => return Err(Error::new(next_token.clone(), "Expected symbol")),
756    };
757    let i164 = match sym1.parse::<i64>() {
758        Ok(s) => s,
759        Err(_) => return Err(Error::new(next_token.clone(), "Expected integer symbol")),
760    };
761    let sym2 = match next_token.term() {
762        Lexicon::Identifier(s) => {
763            next_token = lexer.next_token();
764            s
765        }
766        _ => return Err(Error::new(next_token.clone(), "Expected symbol")),
767    };
768    let i264 = match sym2.parse::<i64>() {
769        Ok(s) => s,
770        Err(_) => return Err(Error::new(next_token.clone(), "Expected integer symbol")),
771    };
772
773    let mut dep_result: Option<Dependency> = None;
774    if let Lexicon::Keyword(k) = next_token.term() {
775        if let Keyword::If = k {
776            if line == token.line() {
777                let (dep, new_token) = parse_dependency(lexer)?;
778
779                dep_result = Some(dep);
780                next_token = new_token;
781            }
782        }
783    }
784    closure(i164, i264, dep_result);
785    Ok(next_token)
786}
787
788/// Converts a given token, if it contains a keyword specifying a configuration
789/// type, to that configuration type.
790fn token_to_config_type(t: &Token) -> Result<ConfigType, Error> {
791    match t.term() {
792        Lexicon::Keyword(k) => match k {
793            Keyword::Bool => Ok(ConfigType::Bool),
794            Keyword::Tristate => Ok(ConfigType::Tristate),
795            Keyword::String => Ok(ConfigType::String),
796            Keyword::Hex => Ok(ConfigType::Hex),
797            Keyword::Int => Ok(ConfigType::Int),
798            _ => Err(Error::new(
799                t.clone(),
800                "Expected type keyword bool, tristate, string, int or hex",
801            )),
802        },
803        _ => Err(Error::new(t.clone(), "Expected keyword")),
804    }
805}
806
807/// Parses the section of a configuration element. The first item read is the name of
808/// the configuration, then the possible settings of the configuration element.
809fn parse_config<I, LB, CE>(hierarchy: &mut I, lexer: &mut LB) -> Result<Token, Error>
810where
811    I: MutableContainer,
812    LB: LexerBase,
813    CE: BaseConfigElement,
814{
815    // Reads a generic configuration item. A generic configuration items needs to start with
816    // a name defining it.
817    let name_token = lexer.next_token();
818
819    // If the next token is not an identifier, something is really off
820    let mut config_element = match name_token.term() {
821        Lexicon::Identifier(n) => CE::create(&n),
822        _ => return Err(Error::new(name_token.clone(), "Expected identifier")),
823    };
824
825    let mut has_more = true;
826    let mut next_token = lexer.next_token();
827    // Loops over all the configuration options, until a non-conforming token is found, then
828    // this token is handed back to parse_element
829    while has_more {
830        let start_token = next_token.clone();
831        match start_token.term() {
832            Lexicon::Help(s) => {
833                config_element.config().help.push_back(s);
834                next_token = lexer.next_token();
835            }
836            Lexicon::Keyword(k) => match k {
837                Keyword::Depends => {
838                    // If the Keyword contains depends, then the next keyword always needs
839                    // to be 'On', if this is not the case, it is an error
840                    next_token = lexer.next_token();
841                    match next_token.term() {
842                        Lexicon::Keyword(k) => match k {
843                            Keyword::On => {
844                                let (d, new_token) = parse_dependency(lexer)?;
845                                config_element.config().dependencies.insert(d);
846                                next_token = new_token;
847                            }
848                            _ => {
849                                return Err(Error::new(name_token.clone(), "Expected keyword 'on'"))
850                            }
851                        },
852                        _ => return Err(Error::new(name_token.clone(), "Expected keyword 'on'")),
853                    }
854                }
855                Keyword::Select => {
856                    next_token = parse_select(lexer, next_token, |s, d| {
857                        config_element
858                            .config()
859                            .reverse_dependencies
860                            .insert(s.to_string(), d.clone());
861                        ()
862                    })?;
863                }
864                Keyword::Imply => {
865                    next_token = parse_select(lexer, next_token, |s, d| {
866                        config_element
867                            .config()
868                            .weak_dependencies
869                            .insert(s.to_string(), d.clone());
870                        ()
871                    })?;
872                }
873                Keyword::Prompt => {
874                    next_token = parse_prompt(lexer, |s| {
875                        config_element.config().prompt = Some(s.to_string());
876                    })?
877                }
878                Keyword::Bool
879                | Keyword::Tristate
880                | Keyword::String
881                | Keyword::Hex
882                | Keyword::Int => {
883                    let mut dependency: Option<Dependency> = None;
884                    next_token = parse_type(
885                        lexer,
886                        next_token,
887                        |s| config_element.config().prompt = Some(s.to_string()),
888                        |d| dependency = Some(d.clone()),
889                    )?;
890                    config_element
891                        .config()
892                        .types
893                        .insert(token_to_config_type(&start_token)?, dependency);
894                }
895                Keyword::Default => {
896                    next_token = parse_default(lexer, next_token, |e, d| {
897                        config_element
898                            .config()
899                            .defaults
900                            .insert(e.clone(), d.map(|x| x.to_owned()));
901                    })?
902                }
903                Keyword::DefBool | Keyword::DefTristate => {
904                    next_token = parse_def_type(lexer, next_token, |e, d| {
905                        match k {
906                            Keyword::DefBool => config_element
907                                .config()
908                                .types
909                                .insert(ConfigType::Bool, d.clone()),
910                            _ => config_element
911                                .config()
912                                .types
913                                .insert(ConfigType::Tristate, d.clone()),
914                        };
915                        config_element
916                            .config()
917                            .defaults
918                            .insert(e.clone(), d.map(|x| x.to_owned()));
919                    })?
920                }
921                Keyword::Range => {
922                    next_token = parse_range(lexer, next_token, |lhs, rhs, dependency| {
923                        config_element.config().range = Some(Range {
924                            lhs,
925                            rhs,
926                            dependency,
927                        })
928                    })?
929                }
930                _ => {
931                    has_more = false;
932                }
933            },
934            _ => {
935                has_more = false;
936            }
937        }
938    }
939
940    config_element.insert_into_hierarchy(hierarchy);
941
942    Ok(next_token)
943}
944
945/// Parses a comment for a menu or for the Ast
946fn parse_comment<C, LB>(commentable: &mut C, lexer: &mut LB) -> Result<Token, Error>
947where
948    C: Commentable,
949    LB: LexerBase,
950{
951    let next_token = lexer.next_token();
952    match next_token.term() {
953        Lexicon::String(s) => {
954            match parse_string(&s) {
955                Ok(v) => commentable.set_comment(&v),
956                Err(e) => return Err(Error::new(next_token.clone(), &e)),
957            };
958            Ok(lexer.next_token())
959        }
960        _ => Err(Error::new(next_token.clone(), "Expected string")),
961    }
962}
963
964fn parse_if<I, LB>(hierarchy: &mut I, lexer: &mut LB) -> Result<Token, Error>
965where
966    I: MutableContainer,
967    LB: LexerBase,
968{
969    // The If block statement requires to contain a dependency, so first
970    // parsing the dependency.
971    let (dependency, mut next_token) = parse_dependency(lexer)?;
972    let mut condition = Condition::new(&dependency);
973    let mut has_more = true;
974    // Loops over all the configuration options, until a non-conforming token is found, then
975    // this token is handed back to parse_element
976    while has_more {
977        let start_token = next_token.clone();
978        match start_token.term() {
979            Lexicon::Keyword(k) => match k {
980                Keyword::Config => {
981                    next_token =
982                        parse_config::<Condition, LB, ConfigElement>(&mut condition, lexer)?
983                }
984                Keyword::Menuconfig => {
985                    next_token =
986                        parse_config::<Condition, LB, MenuConfigElement>(&mut condition, lexer)?
987                }
988                Keyword::Menu => {
989                    // If this keyword is read, it will be a menu block. A menu block has a few items
990                    // which a config or menu config does not have. Also, a menu bock ends with the
991                    // endmenu.
992                    // A menu block can have config and menu config items inside
993
994                    next_token = parse_menu::<Condition, LB>(&mut condition, lexer)?;
995                    // The next token must contain "endmenu"
996                    match next_token.term() {
997                        Lexicon::Keyword(k) => match k {
998                            Keyword::Endmenu => next_token = lexer.next_token(),
999                            _ => {
1000                                return Err(Error::new(
1001                                    next_token.clone(),
1002                                    "Keyword not expected, expected 'endmenu'",
1003                                ))
1004                            }
1005                        },
1006                        _ => return Err(Error::new(next_token.clone(), "Expected keyword")),
1007                    }
1008                }
1009                Keyword::If => {
1010                    // If this keyword is read, it will be a if block. A if block has a few items
1011                    // which a config or if config does not have. Also, a if bock ends with the
1012                    // endif.
1013                    // A if block can have config and if config items inside
1014
1015                    next_token = parse_if::<Condition, LB>(&mut condition, lexer)?;
1016                    // The next token must contain "endif"
1017                    match next_token.term() {
1018                        Lexicon::Keyword(k) => match k {
1019                            Keyword::Endif => next_token = lexer.next_token(),
1020                            _ => {
1021                                return Err(Error::new(
1022                                    next_token.clone(),
1023                                    "Keyword not expected, expected 'endif'",
1024                                ))
1025                            }
1026                        },
1027                        _ => return Err(Error::new(next_token.clone(), "Expected keyword")),
1028                    }
1029                }
1030                _ => {
1031                    has_more = false;
1032                }
1033            },
1034            _ => {
1035                has_more = false;
1036            }
1037        }
1038    }
1039
1040    hierarchy.insert_condition(condition);
1041    Ok(next_token)
1042}
1043
1044fn parse_menu<I, LB>(hierarchy: &mut I, lexer: &mut LB) -> Result<Token, Error>
1045where
1046    I: MutableContainer,
1047    LB: LexerBase,
1048{
1049    // The next token needs to be the name of the menu
1050    let name_token = lexer.next_token();
1051    let mut menu = match name_token.term() {
1052        Lexicon::String(s) => match parse_string(&s) {
1053            Ok(v) => Menu::create(&v),
1054            Err(e) => return Err(Error::new(name_token.clone(), &e)),
1055        },
1056        _ => {
1057            return Err(Error::new(
1058                name_token.clone(),
1059                "Expected character string identifying the menu's name",
1060            ))
1061        }
1062    };
1063
1064    let mut has_more = true;
1065    let mut next_token = lexer.next_token();
1066    // Loops over all the configuration options, until a non-conforming token is found, then
1067    // this token is handed back to parse_element
1068    while has_more {
1069        let start_token = next_token.clone();
1070        match start_token.term() {
1071            Lexicon::Help(s) => {
1072                menu.config().help.push_back(s);
1073                next_token = lexer.next_token();
1074            }
1075            Lexicon::Keyword(k) => match k {
1076                Keyword::Depends => {
1077                    // If the Keyword contains 'Depends', then the next keyword always needs
1078                    // to be 'On', if this is not the case, it is an error
1079                    next_token = lexer.next_token();
1080                    match next_token.term() {
1081                        Lexicon::Keyword(k) => match k {
1082                            Keyword::On => {
1083                                let (d, new_token) = parse_dependency(lexer)?;
1084                                menu.config().dependencies.insert(d);
1085                                next_token = new_token;
1086                            }
1087                            _ => {
1088                                return Err(Error::new(name_token.clone(), "Expected keyword 'on'"))
1089                            }
1090                        },
1091                        _ => return Err(Error::new(name_token.clone(), "Expected keyword 'on'")),
1092                    }
1093                }
1094                Keyword::Visible => {
1095                    // If the Keyword contains 'Visible', then the next keyword always needs
1096                    // to be 'If', if this is not the case, it is an error
1097                    next_token = lexer.next_token();
1098                    match next_token.term() {
1099                        Lexicon::Keyword(k) => match k {
1100                            Keyword::If => {
1101                                let (d, new_token) = parse_dependency(lexer)?;
1102                                menu.visible_if.insert(d);
1103                                next_token = new_token;
1104                            }
1105                            _ => {
1106                                return Err(Error::new(name_token.clone(), "Expected keyword 'if'"))
1107                            }
1108                        },
1109                        _ => return Err(Error::new(name_token.clone(), "Expected keyword 'if'")),
1110                    }
1111                }
1112                Keyword::Comment => next_token = parse_comment::<Menu, LB>(&mut menu, lexer)?,
1113                Keyword::Config => {
1114                    next_token = parse_config::<Menu, LB, ConfigElement>(&mut menu, lexer)?
1115                }
1116                Keyword::Menuconfig => {
1117                    next_token = parse_config::<Menu, LB, MenuConfigElement>(&mut menu, lexer)?
1118                }
1119                Keyword::Menu => {
1120                    // If this keyword is read, it will be a menu block. A menu block has a few items
1121                    // which a config or menu config does not have. Also, a menu bock ends with the
1122                    // endmenu.
1123                    // A menu block can have config and menu config items inside
1124
1125                    next_token = parse_menu::<Menu, LB>(&mut menu, lexer)?;
1126                    // The next token must contain "endmenu"
1127                    match next_token.term() {
1128                        Lexicon::Keyword(k) => match k {
1129                            Keyword::Endmenu => next_token = lexer.next_token(),
1130                            _ => {
1131                                return Err(Error::new(
1132                                    next_token.clone(),
1133                                    "Keyword not expected, expected 'endmenu'",
1134                                ))
1135                            }
1136                        },
1137                        _ => return Err(Error::new(next_token.clone(), "Expected keyword")),
1138                    }
1139                }
1140                Keyword::If => {
1141                    // If this keyword is read, it will be a if block. A if block has a few items
1142                    // which a config or if config does not have. Also, a if bock ends with the
1143                    // endif.
1144                    // A if block can have config and if config items inside
1145
1146                    next_token = parse_if::<Menu, LB>(&mut menu, lexer)?;
1147                    // The next token must contain "endif"
1148                    match next_token.term() {
1149                        Lexicon::Keyword(k) => match k {
1150                            Keyword::Endif => next_token = lexer.next_token(),
1151                            _ => {
1152                                return Err(Error::new(
1153                                    next_token.clone(),
1154                                    "Keyword not expected, expected 'endif'",
1155                                ))
1156                            }
1157                        },
1158                        _ => return Err(Error::new(next_token.clone(), "Expected keyword")),
1159                    }
1160                }
1161                _ => {
1162                    has_more = false;
1163                }
1164            },
1165            _ => {
1166                has_more = false;
1167            }
1168        }
1169    }
1170
1171    menu.insert_into_hierarchy(hierarchy);
1172    Ok(next_token)
1173}
1174
1175impl Ast {
1176    fn read_main_menu<LB>(&mut self, lexer: &mut LB) -> Result<Token, Error>
1177    where
1178        LB: LexerBase,
1179    {
1180        let t = lexer.next_token();
1181        match t.term() {
1182            Lexicon::Keyword(k) => match k {
1183                Keyword::Mainmenu => {
1184                    self.main_menu = match parse_string(&should_be_string(&lexer.next_token())?) {
1185                        Ok(v) => Some(v),
1186                        Err(e) => return Err(Error::new(t.clone(), &e)),
1187                    };
1188                    Ok(lexer.next_token())
1189                }
1190                _ => Ok(t),
1191            },
1192            _ => Ok(t),
1193        }
1194    }
1195
1196    pub fn element(&self, name: &str) -> Option<&Element> {
1197        for (elt_name, elt) in &self.elements {
1198            if elt_name == name {
1199                return Some(&elt);
1200            }
1201        }
1202        None
1203    }
1204
1205    pub fn contains_element(&self, name: &str) -> bool {
1206        match self.element(name) {
1207            Some(_) => true,
1208            None => false,
1209        }
1210    }
1211
1212    pub fn len(&self) -> usize {
1213        self.elements.len()
1214    }
1215
1216    fn parse_element<LB>(&mut self, token: Token, lexer: &mut LB) -> Result<Token, Error>
1217    where
1218        LB: LexerBase,
1219    {
1220        match token.term() {
1221            // The first token needs to be a keyword, precicely, it needs to be any of the following:
1222            // - config - Defines a newly defined configuration item within the current menu hierarchy
1223            // - menuconfig - Defines a newly defined configuration item, with possible suboptions
1224            // - choice - Defines a choice item, which only allows choice options (boolean or tristate)
1225            // - comment - Defines a comment item
1226            // - menu - Defines a new menu, which allows for a menu structure, it is not a configuration item
1227            // - if - Evaluates an expression, and if evaluated to true, replaces itself with its body
1228            // - source - Inserts the given source file (even when found within an if statement)
1229            Lexicon::Keyword(k) => match k {
1230                Keyword::Config => parse_config::<Ast, LB, ConfigElement>(self, lexer),
1231                Keyword::Menuconfig => parse_config::<Ast, LB, MenuConfigElement>(self, lexer),
1232                Keyword::Menu => {
1233                    // If this keyword is read, it will be a menu block. A menu block has a few items
1234                    // which a config or menu config does not have. Also, a menu bock ends with the
1235                    // endmenu.
1236                    // A menu block can have config and menu config items inside
1237
1238                    let next_token = parse_menu::<Ast, LB>(self, lexer)?;
1239                    // The next token must contain "endmenu"
1240                    match next_token.term() {
1241                        Lexicon::Keyword(k) => match k {
1242                            Keyword::Endmenu => Ok(lexer.next_token()),
1243                            _ => Err(Error::new(next_token.clone(), "Keyword not expected, expected 'endmenu'"))
1244                        },
1245                        _ => Err(Error::new(next_token.clone(), "Expected keyword"))
1246                    }
1247                }
1248                Keyword::If => {
1249                    // If this keyword is read, it will be a if block. A if block has a few items
1250                    // which a config or if config does not have. Also, a if bock ends with the
1251                    // endif.
1252                    // A if block can have config and if config items inside
1253
1254                    let next_token = parse_if::<Ast, LB>(self, lexer)?;
1255                    // The next token must contain "endif"
1256                    match next_token.term() {
1257                        Lexicon::Keyword(k) => match k {
1258                            Keyword::Endif => Ok(lexer.next_token()),
1259                            _ => Err(Error::new(next_token.clone(), "Keyword not expected, expected 'endif'"))
1260                        },
1261                        _ => Err(Error::new(next_token.clone(), "Expected keyword"))
1262                    }
1263                }
1264                Keyword::Comment => parse_comment(self, lexer),
1265                _ => Err(Error::new(token.clone(), "Keyword not expected, expected 'config', 'menuconfig', 'choice', 'comment', 'menu', 'if', 'source'"))
1266            },
1267            Lexicon::Error(e) => Err(Error::new(token.clone(), &format!("Erroneous element: {}", e))),
1268            _ => Err(Error::new(token.clone(), "Expected keyword"))
1269        }
1270    }
1271
1272    fn read_actual<LB>(&mut self, lexer: &mut LB) -> Result<(), Error>
1273    where
1274        LB: LexerBase,
1275    {
1276        // The first token being read should either be the "mainmenu" token
1277        // followed by a string, or it should be a normal token.
1278
1279        let mut next_token = self.read_main_menu(lexer)?;
1280
1281        while !next_token.eot() {
1282            next_token = self.parse_element(next_token, lexer)?;
1283        }
1284
1285        Ok(())
1286    }
1287
1288    /// Creates an empty, non-usable Ast
1289    fn new() -> Self {
1290        Self {
1291            main_menu: None,
1292            elements: Vec::new(),
1293            comment: String::new(),
1294            conditions: Vec::new(),
1295        }
1296    }
1297
1298    /// Parses the Ast from a stream which implements the Read trait.
1299    pub fn parse<LB>(input: &mut LB) -> Result<Self, Error>
1300    where
1301        LB: LexerBase,
1302    {
1303        let mut ast = Ast::new();
1304        ast.read_actual(input)?;
1305        Ok(ast)
1306    }
1307
1308    pub fn main_menu(&self) -> Option<&String> {
1309        return self.main_menu.as_ref();
1310    }
1311}
1312
1313#[cfg(test)]
1314mod tests {
1315    use super::super::super::lex::lexer::Lexer;
1316    use super::*;
1317
1318    fn ast_from(s: &str) -> Result<Ast, Error> {
1319        let mut lexer = Lexer::create(s.as_bytes());
1320        Ok(Ast::parse(&mut lexer)?)
1321    }
1322
1323    fn elements_vec_to_hashmap(v: Vec<(String, Element)>) -> HashMap<String, Element> {
1324        v.iter().map(|tuple| tuple.clone()).collect()
1325    }
1326
1327    #[test]
1328    fn test_mainmenu() -> Result<(), Error> {
1329        let ast = ast_from(&"mainmenu \"Hello World\"")?;
1330        assert_eq!(ast.main_menu(), Some(&"Hello World".to_string()));
1331        Ok(())
1332    }
1333
1334    #[test]
1335    fn test_parse_config() -> Result<(), Error> {
1336        let ast = ast_from(&"config MODVERSIONS")?;
1337
1338        assert_eq!(ast.len(), 1);
1339        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1340        let elt = ast.element("MODVERSIONS").unwrap();
1341        assert_eq!(elt.elt_type(), ElementType::Config);
1342        Ok(())
1343    }
1344
1345    #[test]
1346    fn test_parse_menuconfig() -> Result<(), Error> {
1347        let ast = ast_from(&"menuconfig MODVERSIONS")?;
1348
1349        assert_eq!(ast.len(), 1);
1350        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1351        let elt = ast.element("MODVERSIONS").unwrap();
1352        assert_eq!(elt.elt_type(), ElementType::MenuConfig);
1353        Ok(())
1354    }
1355
1356    #[test]
1357    fn test_parse_config_dependencies() -> Result<(), Error> {
1358        let ast = ast_from(&"config MODVERSIONS depends on MODULES")?;
1359
1360        assert_eq!(ast.len(), 1);
1361        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1362        let elt = ast.element("MODVERSIONS").unwrap();
1363        let deps = elt.dependencies();
1364        assert_eq!(deps.len(), 1);
1365        let expected_dep = Dependency::new(&Expr::Sym("MODULES".to_string()));
1366        assert_eq!(deps.contains(&expected_dep), true);
1367        Ok(())
1368    }
1369
1370    #[test]
1371    fn test_parse_config_multiple_dependencies() -> Result<(), Error> {
1372        let ast = ast_from(&"config MODVERSIONS depends on FOO && BAR")?;
1373
1374        assert_eq!(ast.len(), 1);
1375        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1376        let elt = ast.element("MODVERSIONS").unwrap();
1377        let deps = elt.dependencies();
1378        assert_eq!(deps.len(), 1);
1379        let foo_subexpr = Expr::Sym("FOO".to_string());
1380        let bar_subexpr = Expr::Sym("BAR".to_string());
1381        for dep in deps {
1382            assert_eq!(dep.expr().is_and(), true);
1383            assert_eq!(dep.len(), 2);
1384            assert_eq!(dep.contains(&foo_subexpr), true);
1385            assert_eq!(dep.contains(&bar_subexpr), true);
1386        }
1387        Ok(())
1388    }
1389
1390    #[test]
1391    fn test_parse_config_prompt() -> Result<(), Error> {
1392        let ast = ast_from(&"config MODVERSIONS prompt \"Hello World\"")?;
1393
1394        assert_eq!(ast.len(), 1);
1395        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1396        let elt = ast.element("MODVERSIONS").unwrap();
1397        assert_ne!(elt.prompt(), None);
1398        let prompt = elt.prompt().unwrap();
1399        assert_eq!(prompt, "Hello World".to_string());
1400        Ok(())
1401    }
1402
1403    #[test]
1404    fn test_parse_bool() -> Result<(), Error> {
1405        let ast = ast_from(&"config MODVERSIONS \n prompt \"Hello World\" \nbool")?;
1406
1407        assert_eq!(ast.len(), 1);
1408        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1409        let elt = ast.element("MODVERSIONS").unwrap();
1410        assert_ne!(elt.prompt(), None);
1411        let prompt = elt.prompt().unwrap();
1412        assert_eq!(prompt, "Hello World".to_string());
1413        assert_eq!(elt.types().unwrap().get(&ConfigType::Bool).unwrap(), &None);
1414        Ok(())
1415    }
1416
1417    #[test]
1418    fn test_parse_bool_with_prompt() -> Result<(), Error> {
1419        let ast = ast_from(&"config MODVERSIONS \nbool \"Hello World\"")?;
1420
1421        assert_eq!(ast.len(), 1);
1422        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1423        let elt = ast.element("MODVERSIONS").unwrap();
1424        assert_ne!(elt.prompt(), None);
1425        let prompt = elt.prompt().unwrap();
1426        assert_eq!(prompt, "Hello World".to_string());
1427        assert_eq!(elt.types().unwrap().get(&ConfigType::Bool).unwrap(), &None);
1428        Ok(())
1429    }
1430
1431    #[test]
1432    fn test_parse_bool_with_if_dependency() -> Result<(), Error> {
1433        let ast = ast_from(&"config MODVERSIONS \nbool if FOO")?;
1434
1435        assert_eq!(ast.len(), 1);
1436        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1437        let elt = ast.element("MODVERSIONS").unwrap();
1438        assert_eq!(elt.prompt(), None);
1439        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
1440        assert_eq!(
1441            elt.types().unwrap().get(&ConfigType::Bool).unwrap(),
1442            &Some(expected_dep)
1443        );
1444        Ok(())
1445    }
1446
1447    #[test]
1448    fn test_parse_bool_with_prompt_and_if_dependency() -> Result<(), Error> {
1449        let ast = ast_from(&"config MODVERSIONS \ntristate \"Hello World\" if FOO")?;
1450
1451        assert_eq!(ast.len(), 1);
1452        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1453        let elt = ast.element("MODVERSIONS").unwrap();
1454        assert_ne!(elt.prompt(), None);
1455        let prompt = elt.prompt().unwrap();
1456        assert_eq!(prompt, "Hello World".to_string());
1457        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
1458        assert_eq!(
1459            elt.types().unwrap().get(&ConfigType::Tristate).unwrap(),
1460            &Some(expected_dep)
1461        );
1462        Ok(())
1463    }
1464
1465    #[test]
1466    fn test_parse_tristate() -> Result<(), Error> {
1467        let ast = ast_from(&"config MODVERSIONS \n prompt \"Hello World\" \ntristate")?;
1468
1469        assert_eq!(ast.len(), 1);
1470        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1471        let elt = ast.element("MODVERSIONS").unwrap();
1472        assert_ne!(elt.prompt(), None);
1473        let prompt = elt.prompt().unwrap();
1474        assert_eq!(prompt, "Hello World".to_string());
1475        assert_eq!(
1476            elt.types().unwrap().get(&ConfigType::Tristate).unwrap(),
1477            &None
1478        );
1479        Ok(())
1480    }
1481
1482    #[test]
1483    fn test_parse_tristate_with_prompt() -> Result<(), Error> {
1484        let ast = ast_from(&"config MODVERSIONS \ntristate \"Hello World\"")?;
1485
1486        assert_eq!(ast.len(), 1);
1487        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1488        let elt = ast.element("MODVERSIONS").unwrap();
1489        assert_ne!(elt.prompt(), None);
1490        let prompt = elt.prompt().unwrap();
1491        assert_eq!(prompt, "Hello World".to_string());
1492        assert_eq!(
1493            elt.types().unwrap().get(&ConfigType::Tristate).unwrap(),
1494            &None
1495        );
1496        Ok(())
1497    }
1498
1499    #[test]
1500    fn test_parse_tristate_with_if_dependency() -> Result<(), Error> {
1501        let ast = ast_from(&"config MODVERSIONS \ntristate if FOO")?;
1502
1503        assert_eq!(ast.len(), 1);
1504        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1505        let elt = ast.element("MODVERSIONS").unwrap();
1506        assert_eq!(elt.prompt(), None);
1507        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
1508        assert_eq!(
1509            elt.types().unwrap().get(&ConfigType::Tristate).unwrap(),
1510            &Some(expected_dep)
1511        );
1512        Ok(())
1513    }
1514
1515    #[test]
1516    fn test_parse_tristate_with_prompt_and_if_dependency() -> Result<(), Error> {
1517        let ast = ast_from(&"config MODVERSIONS \ntristate \"Hello World\" if FOO")?;
1518
1519        assert_eq!(ast.len(), 1);
1520        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1521        let elt = ast.element("MODVERSIONS").unwrap();
1522        assert_ne!(elt.prompt(), None);
1523        let prompt = elt.prompt().unwrap();
1524        assert_eq!(prompt, "Hello World".to_string());
1525        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
1526        assert_eq!(
1527            elt.types().unwrap().get(&ConfigType::Tristate).unwrap(),
1528            &Some(expected_dep)
1529        );
1530        Ok(())
1531    }
1532
1533    #[test]
1534    fn test_parse_string() -> Result<(), Error> {
1535        let ast = ast_from(&"config MODVERSIONS \n prompt \"Hello World\" \nstring")?;
1536
1537        assert_eq!(ast.len(), 1);
1538        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1539        let elt = ast.element("MODVERSIONS").unwrap();
1540        assert_ne!(elt.prompt(), None);
1541        let prompt = elt.prompt().unwrap();
1542        assert_eq!(prompt, "Hello World".to_string());
1543        assert_eq!(
1544            elt.types().unwrap().get(&ConfigType::String).unwrap(),
1545            &None
1546        );
1547        Ok(())
1548    }
1549
1550    #[test]
1551    fn test_parse_string_with_prompt() -> Result<(), Error> {
1552        let ast = ast_from(&"config MODVERSIONS \nstring \"Hello World\"")?;
1553
1554        assert_eq!(ast.len(), 1);
1555        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1556        let elt = ast.element("MODVERSIONS").unwrap();
1557        assert_ne!(elt.prompt(), None);
1558        let prompt = elt.prompt().unwrap();
1559        assert_eq!(prompt, "Hello World".to_string());
1560        assert_eq!(
1561            elt.types().unwrap().get(&ConfigType::String).unwrap(),
1562            &None
1563        );
1564        Ok(())
1565    }
1566
1567    #[test]
1568    fn test_parse_string_with_if_dependency() -> Result<(), Error> {
1569        let ast = ast_from(&"config MODVERSIONS \nstring if FOO")?;
1570
1571        assert_eq!(ast.len(), 1);
1572        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1573        let elt = ast.element("MODVERSIONS").unwrap();
1574        assert_eq!(elt.prompt(), None);
1575        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
1576        assert_eq!(
1577            elt.types().unwrap().get(&ConfigType::String).unwrap(),
1578            &Some(expected_dep)
1579        );
1580        Ok(())
1581    }
1582
1583    #[test]
1584    fn test_parse_string_with_prompt_and_if_dependency() -> Result<(), Error> {
1585        let ast = ast_from(&"config MODVERSIONS \nstring \"Hello World\" if FOO")?;
1586
1587        assert_eq!(ast.len(), 1);
1588        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1589        let elt = ast.element("MODVERSIONS").unwrap();
1590        assert_ne!(elt.prompt(), None);
1591        let prompt = elt.prompt().unwrap();
1592        assert_eq!(prompt, "Hello World".to_string());
1593        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
1594        assert_eq!(
1595            elt.types().unwrap().get(&ConfigType::String).unwrap(),
1596            &Some(expected_dep)
1597        );
1598        Ok(())
1599    }
1600
1601    #[test]
1602    fn test_parse_int() -> Result<(), Error> {
1603        let ast = ast_from(&"config MODVERSIONS \n prompt \"Hello World\" \nint")?;
1604
1605        assert_eq!(ast.len(), 1);
1606        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1607        let elt = ast.element("MODVERSIONS").unwrap();
1608        assert_ne!(elt.prompt(), None);
1609        let prompt = elt.prompt().unwrap();
1610        assert_eq!(prompt, "Hello World".to_string());
1611        assert_eq!(elt.types().unwrap().get(&ConfigType::Int).unwrap(), &None);
1612        Ok(())
1613    }
1614
1615    #[test]
1616    fn test_parse_int_with_prompt() -> Result<(), Error> {
1617        let ast = ast_from(&"config MODVERSIONS \nint \"Hello World\"")?;
1618
1619        assert_eq!(ast.len(), 1);
1620        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1621        let elt = ast.element("MODVERSIONS").unwrap();
1622        assert_ne!(elt.prompt(), None);
1623        let prompt = elt.prompt().unwrap();
1624        assert_eq!(prompt, "Hello World".to_string());
1625        assert_eq!(elt.types().unwrap().get(&ConfigType::Int).unwrap(), &None);
1626        Ok(())
1627    }
1628
1629    #[test]
1630    fn test_parse_int_with_if_dependency() -> Result<(), Error> {
1631        let ast = ast_from(&"config MODVERSIONS \nint if FOO")?;
1632
1633        assert_eq!(ast.len(), 1);
1634        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1635        let elt = ast.element("MODVERSIONS").unwrap();
1636        assert_eq!(elt.prompt(), None);
1637        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
1638        assert_eq!(
1639            elt.types().unwrap().get(&ConfigType::Int).unwrap(),
1640            &Some(expected_dep)
1641        );
1642        Ok(())
1643    }
1644
1645    #[test]
1646    fn test_parse_int_with_prompt_and_if_dependency() -> Result<(), Error> {
1647        let ast = ast_from(&"config MODVERSIONS \nint \"Hello World\" if FOO")?;
1648
1649        assert_eq!(ast.len(), 1);
1650        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1651        let elt = ast.element("MODVERSIONS").unwrap();
1652        assert_ne!(elt.prompt(), None);
1653        let prompt = elt.prompt().unwrap();
1654        assert_eq!(prompt, "Hello World".to_string());
1655        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
1656        assert_eq!(
1657            elt.types().unwrap().get(&ConfigType::Int).unwrap(),
1658            &Some(expected_dep)
1659        );
1660        Ok(())
1661    }
1662
1663    #[test]
1664    fn test_parse_hex() -> Result<(), Error> {
1665        let ast = ast_from(&"config MODVERSIONS \n prompt \"Hello World\" \nhex")?;
1666
1667        assert_eq!(ast.len(), 1);
1668        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1669        let elt = ast.element("MODVERSIONS").unwrap();
1670        assert_ne!(elt.prompt(), None);
1671        let prompt = elt.prompt().unwrap();
1672        assert_eq!(prompt, "Hello World".to_string());
1673        assert_eq!(elt.types().unwrap().get(&ConfigType::Hex).unwrap(), &None);
1674        Ok(())
1675    }
1676
1677    #[test]
1678    fn test_parse_hex_with_prompt() -> Result<(), Error> {
1679        let ast = ast_from(&"config MODVERSIONS \nhex \"Hello World\"")?;
1680
1681        assert_eq!(ast.len(), 1);
1682        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1683        let elt = ast.element("MODVERSIONS").unwrap();
1684        assert_ne!(elt.prompt(), None);
1685        let prompt = elt.prompt().unwrap();
1686        assert_eq!(prompt, "Hello World".to_string());
1687        assert_eq!(elt.types().unwrap().get(&ConfigType::Hex).unwrap(), &None);
1688        Ok(())
1689    }
1690
1691    #[test]
1692    fn test_parse_hex_with_if_dependency() -> Result<(), Error> {
1693        let ast = ast_from(&"config MODVERSIONS \nhex if FOO")?;
1694
1695        assert_eq!(ast.len(), 1);
1696        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1697        let elt = ast.element("MODVERSIONS").unwrap();
1698        assert_eq!(elt.prompt(), None);
1699        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
1700        assert_eq!(
1701            elt.types().unwrap().get(&ConfigType::Hex).unwrap(),
1702            &Some(expected_dep)
1703        );
1704        Ok(())
1705    }
1706
1707    #[test]
1708    fn test_parse_hex_with_prompt_and_if_dependency() -> Result<(), Error> {
1709        let ast = ast_from(&"config MODVERSIONS \nhex \"Hello World\" if FOO")?;
1710
1711        assert_eq!(ast.len(), 1);
1712        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1713        let elt = ast.element("MODVERSIONS").unwrap();
1714        assert_ne!(elt.prompt(), None);
1715        let prompt = elt.prompt().unwrap();
1716        assert_eq!(prompt, "Hello World".to_string());
1717        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
1718        assert_eq!(
1719            elt.types().unwrap().get(&ConfigType::Hex).unwrap(),
1720            &Some(expected_dep)
1721        );
1722        Ok(())
1723    }
1724
1725    #[test]
1726    fn test_parse_default() -> Result<(), Error> {
1727        let ast = ast_from(&"config MODVERSIONS \n default y")?;
1728
1729        assert_eq!(ast.len(), 1);
1730        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1731        let elt = ast.element("MODVERSIONS").unwrap();
1732        assert_eq!(
1733            elt.defaults()
1734                .unwrap()
1735                .get(&Expr::Sym("y".to_string()))
1736                .unwrap(),
1737            &None
1738        );
1739        Ok(())
1740    }
1741
1742    #[test]
1743    fn test_parse_default_string() -> Result<(), Error> {
1744        let ast = ast_from(&"config MODVERSIONS \n default \"y\"")?;
1745
1746        assert_eq!(ast.len(), 1);
1747        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1748        let elt = ast.element("MODVERSIONS").unwrap();
1749        assert_eq!(
1750            elt.defaults()
1751                .unwrap()
1752                .get(&Expr::Sym("y".to_string()))
1753                .unwrap(),
1754            &None
1755        );
1756        Ok(())
1757    }
1758
1759    #[test]
1760    fn test_parse_default_with_if_dependency() -> Result<(), Error> {
1761        let ast = ast_from(&"config MODVERSIONS \nbool default y if FOO")?;
1762
1763        assert_eq!(ast.len(), 1);
1764        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1765        let elt = ast.element("MODVERSIONS").unwrap();
1766        let key = Expr::Sym("y".to_string());
1767        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
1768        assert_eq!(
1769            elt.defaults().unwrap().get(&key).unwrap(),
1770            &Some(expected_dep)
1771        );
1772        Ok(())
1773    }
1774
1775    #[test]
1776    fn test_parse_def_bool() -> Result<(), Error> {
1777        let ast = ast_from(&"config MODVERSIONS \n def_bool y")?;
1778
1779        assert_eq!(ast.len(), 1);
1780        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1781        let elt = ast.element("MODVERSIONS").unwrap();
1782        assert_eq!(elt.types().unwrap().get(&ConfigType::Bool).unwrap(), &None);
1783        assert_eq!(
1784            elt.defaults()
1785                .unwrap()
1786                .get(&Expr::Sym("y".to_string()))
1787                .unwrap(),
1788            &None
1789        );
1790        Ok(())
1791    }
1792
1793    #[test]
1794    fn test_parse_def_bool_with_dependency() -> Result<(), Error> {
1795        let ast = ast_from(&"config MODVERSIONS \ndef_bool y if FOO")?;
1796
1797        assert_eq!(ast.len(), 1);
1798        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1799        let elt = ast.element("MODVERSIONS").unwrap();
1800        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
1801        assert_eq!(
1802            elt.types().unwrap().get(&ConfigType::Bool).unwrap(),
1803            &Some(expected_dep.clone())
1804        );
1805        let key = Expr::Sym("y".to_string());
1806        assert_eq!(
1807            elt.defaults().unwrap().get(&key).unwrap(),
1808            &Some(expected_dep)
1809        );
1810        Ok(())
1811    }
1812
1813    #[test]
1814    fn test_parse_def_tristate() -> Result<(), Error> {
1815        let ast = ast_from(&"config MODVERSIONS \n def_tristate y")?;
1816
1817        assert_eq!(ast.len(), 1);
1818        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1819        let elt = ast.element("MODVERSIONS").unwrap();
1820        assert_eq!(
1821            elt.types().unwrap().get(&ConfigType::Tristate).unwrap(),
1822            &None
1823        );
1824        assert_eq!(
1825            elt.defaults()
1826                .unwrap()
1827                .get(&Expr::Sym("y".to_string()))
1828                .unwrap(),
1829            &None
1830        );
1831        Ok(())
1832    }
1833
1834    #[test]
1835    fn test_parse_def_tristate_with_dependency() -> Result<(), Error> {
1836        let ast = ast_from(&"config MODVERSIONS \ndef_tristate y if FOO")?;
1837
1838        assert_eq!(ast.len(), 1);
1839        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1840        let elt = ast.element("MODVERSIONS").unwrap();
1841        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
1842        assert_eq!(
1843            elt.types().unwrap().get(&ConfigType::Tristate).unwrap(),
1844            &Some(expected_dep.clone())
1845        );
1846        let key = Expr::Sym("y".to_string());
1847        assert_eq!(
1848            elt.defaults().unwrap().get(&key).unwrap(),
1849            &Some(expected_dep)
1850        );
1851        Ok(())
1852    }
1853
1854    #[test]
1855    fn test_parse_select() -> Result<(), Error> {
1856        let ast = ast_from(&"config MODVERSIONS \n select y")?;
1857
1858        assert_eq!(ast.len(), 1);
1859        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1860        let elt = ast.element("MODVERSIONS").unwrap();
1861        assert_eq!(
1862            elt.reverse_dependencies()
1863                .unwrap()
1864                .get(&"y".to_string())
1865                .unwrap(),
1866            &None
1867        );
1868        Ok(())
1869    }
1870
1871    #[test]
1872    fn test_parse_select_with_dependency() -> Result<(), Error> {
1873        let ast = ast_from(&"config MODVERSIONS \nselect y if FOO")?;
1874
1875        assert_eq!(ast.len(), 1);
1876        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1877        let elt = ast.element("MODVERSIONS").unwrap();
1878        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
1879        assert_eq!(
1880            elt.reverse_dependencies()
1881                .unwrap()
1882                .get(&"y".to_string())
1883                .unwrap(),
1884            &Some(expected_dep)
1885        );
1886        Ok(())
1887    }
1888
1889    #[test]
1890    fn test_parse_imply() -> Result<(), Error> {
1891        let ast = ast_from(&"config MODVERSIONS \n imply y")?;
1892
1893        assert_eq!(ast.len(), 1);
1894        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1895        let elt = ast.element("MODVERSIONS").unwrap();
1896        assert_eq!(
1897            elt.weak_dependencies()
1898                .unwrap()
1899                .get(&"y".to_string())
1900                .unwrap(),
1901            &None
1902        );
1903        Ok(())
1904    }
1905
1906    #[test]
1907    fn test_parse_imply_with_dependency() -> Result<(), Error> {
1908        let ast = ast_from(&"config MODVERSIONS \nimply y if FOO")?;
1909
1910        assert_eq!(ast.len(), 1);
1911        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1912        let elt = ast.element("MODVERSIONS").unwrap();
1913        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
1914        assert_eq!(
1915            elt.weak_dependencies()
1916                .unwrap()
1917                .get(&"y".to_string())
1918                .unwrap(),
1919            &Some(expected_dep)
1920        );
1921        Ok(())
1922    }
1923
1924    #[test]
1925    fn test_parse_range() -> Result<(), Error> {
1926        let ast = ast_from(&"config MODVERSIONS \n range 1 2")?;
1927
1928        assert_eq!(ast.len(), 1);
1929        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1930        let elt = ast.element("MODVERSIONS").unwrap();
1931        assert_eq!(elt.range().unwrap().lhs(), 1);
1932        assert_eq!(elt.range().unwrap().rhs(), 2);
1933        assert_eq!(elt.range().unwrap().dependency(), &None);
1934        Ok(())
1935    }
1936
1937    #[test]
1938    fn test_negative_to_positive_range() -> Result<(), Error> {
1939        let ast = ast_from(&"config MODVERSIONS \n range -1 1")?;
1940
1941        assert_eq!(ast.len(), 1);
1942        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1943        let elt = ast.element("MODVERSIONS").unwrap();
1944        assert_eq!(elt.range().unwrap().lhs(), -1);
1945        assert_eq!(elt.range().unwrap().rhs(), 1);
1946        assert_eq!(elt.range().unwrap().dependency(), &None);
1947        Ok(())
1948    }
1949
1950    #[test]
1951    fn test_negative_range() -> Result<(), Error> {
1952        let ast = ast_from(&"config MODVERSIONS \n range -2 -1")?;
1953
1954        assert_eq!(ast.len(), 1);
1955        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1956        let elt = ast.element("MODVERSIONS").unwrap();
1957        assert_eq!(elt.range().unwrap().lhs(), -2);
1958        assert_eq!(elt.range().unwrap().rhs(), -1);
1959        assert_eq!(elt.range().unwrap().dependency(), &None);
1960        Ok(())
1961    }
1962
1963    #[test]
1964    fn test_parse_range_with_dependency() -> Result<(), Error> {
1965        let ast = ast_from(&"config MODVERSIONS \nrange 1 2 if FOO")?;
1966
1967        assert_eq!(ast.len(), 1);
1968        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1969        let elt = ast.element("MODVERSIONS").unwrap();
1970        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
1971        assert_eq!(elt.range().unwrap().lhs(), 1);
1972        assert_eq!(elt.range().unwrap().rhs(), 2);
1973        assert_eq!(elt.range().unwrap().dependency(), &Some(expected_dep));
1974        Ok(())
1975    }
1976
1977    #[test]
1978    fn test_parse_help() -> Result<(), Error> {
1979        let ast = ast_from(&"config MODVERSIONS \nhelp\n    foo\n    bar\n")?;
1980
1981        assert_eq!(ast.len(), 1);
1982        assert_eq!(ast.contains_element("MODVERSIONS"), true);
1983        let elt = ast.element("MODVERSIONS").unwrap();
1984        assert_eq!(
1985            elt.help().into_iter().next().unwrap(),
1986            "foo\nbar".to_string()
1987        );
1988        Ok(())
1989    }
1990
1991    #[test]
1992    fn test_parse_comment() -> Result<(), Error> {
1993        let ast = ast_from(&"comment \"Foo\"")?;
1994
1995        assert_eq!(ast.comment(), "Foo".to_string());
1996        Ok(())
1997    }
1998
1999    #[test]
2000    fn test_parse_simple_if() -> Result<(), Error> {
2001        let ast = ast_from(&"if FOO endif")?;
2002
2003        assert_eq!(ast.len_conditional_blocks(), 1);
2004        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
2005        let mut passes = 0;
2006        for block in ast.conditional_blocks() {
2007            assert_eq!(block.dependency(), expected_dep);
2008            passes += 1;
2009        }
2010        assert_eq!(passes, 1);
2011        Ok(())
2012    }
2013
2014    #[test]
2015    fn test_simple_menu() -> Result<(), Error> {
2016        let ast = ast_from(&"menu \"Menu\"\nendmenu")?;
2017
2018        assert_eq!(ast.len(), 1);
2019        assert_eq!(ast.contains_element("Menu"), true);
2020        let elt = ast.element("Menu").unwrap();
2021        assert_eq!(elt.elt_type(), ElementType::Menu);
2022        Ok(())
2023    }
2024
2025    #[test]
2026    fn test_menu_depends_on() -> Result<(), Error> {
2027        let ast = ast_from(&"menu \"Menu\" depends on MODULES \nendmenu")?;
2028
2029        assert_eq!(ast.len(), 1);
2030        assert_eq!(ast.contains_element("Menu"), true);
2031        let elt = ast.element("Menu").unwrap();
2032        let deps = elt.dependencies();
2033        assert_eq!(deps.len(), 1);
2034        let expected_dep = Dependency::new(&Expr::Sym("MODULES".to_string()));
2035        assert_eq!(deps.contains(&expected_dep), true);
2036        Ok(())
2037    }
2038
2039    #[test]
2040    fn test_menu_visible_if() -> Result<(), Error> {
2041        let ast = ast_from(&"menu \"Menu\" visible if MODULES \nendmenu")?;
2042
2043        assert_eq!(ast.len(), 1);
2044        assert_eq!(ast.contains_element("Menu"), true);
2045        let elt = ast.element("Menu").unwrap();
2046        let visible_deps = elt.visible_if();
2047        assert_eq!(visible_deps.len(), 1);
2048        let expected_dep = Dependency::new(&Expr::Sym("MODULES".to_string()));
2049        assert_eq!(visible_deps.contains(&expected_dep), true);
2050        Ok(())
2051    }
2052
2053    #[test]
2054    fn test_menu_with_config() -> Result<(), Error> {
2055        let ast = ast_from(&"menu \"Menu\" config FOO \nendmenu")?;
2056
2057        assert_eq!(ast.len(), 1);
2058        assert_eq!(ast.contains_element("Menu"), true);
2059        let elt = ast.element("Menu").unwrap();
2060        let sub_elements = elements_vec_to_hashmap(elt.sub_elements());
2061        assert_eq!(sub_elements.len(), 1);
2062        assert_eq!(sub_elements.contains_key("FOO"), true);
2063        let sub_elt = sub_elements.get("FOO").unwrap();
2064        assert_eq!(sub_elt.elt_type(), ElementType::Config);
2065        assert_eq!(sub_elt.name(), "FOO".to_string());
2066        Ok(())
2067    }
2068
2069    #[test]
2070    fn test_menu_with_menuconfig() -> Result<(), Error> {
2071        let ast = ast_from(&"menu \"Menu\" menuconfig FOO \nendmenu")?;
2072
2073        assert_eq!(ast.len(), 1);
2074        assert_eq!(ast.contains_element("Menu"), true);
2075        let elt = ast.element("Menu").unwrap();
2076        let sub_elements = elements_vec_to_hashmap(elt.sub_elements());
2077        assert_eq!(sub_elements.len(), 1);
2078        assert_eq!(sub_elements.contains_key("FOO"), true);
2079        let sub_elt = sub_elements.get("FOO").unwrap();
2080        assert_eq!(sub_elt.elt_type(), ElementType::MenuConfig);
2081        assert_eq!(sub_elt.name(), "FOO".to_string());
2082        Ok(())
2083    }
2084
2085    #[test]
2086    fn test_menu_with_submenu() -> Result<(), Error> {
2087        let ast = ast_from(&"menu \"Menu\"\nmenu \"Submenu\" endmenu \nendmenu")?;
2088
2089        assert_eq!(ast.len(), 1);
2090        assert_eq!(ast.contains_element("Menu"), true);
2091        let elt = ast.element("Menu").unwrap();
2092        let sub_elements = elements_vec_to_hashmap(elt.sub_elements());
2093        assert_eq!(sub_elements.len(), 1);
2094        assert_eq!(sub_elements.contains_key("Submenu"), true);
2095        let sub_elt = sub_elements.get("Submenu").unwrap();
2096        assert_eq!(sub_elt.elt_type(), ElementType::Menu);
2097        assert_eq!(sub_elt.name(), "Submenu".to_string());
2098        Ok(())
2099    }
2100
2101    #[test]
2102    fn test_menu_comment() -> Result<(), Error> {
2103        let ast = ast_from(&"menu \"Menu\" comment \"Foo\" endmenu")?;
2104
2105        let elt = ast.element("Menu").unwrap();
2106        assert_eq!(elt.elt_type(), ElementType::Menu);
2107        if let Element::Menu(menu) = elt {
2108            assert_eq!(menu.comment(), "Foo".to_string());
2109        }
2110        Ok(())
2111    }
2112
2113    #[test]
2114    fn test_menu_simple_if() -> Result<(), Error> {
2115        let ast = ast_from(&"menu \"Menu\" if FOO endif endmenu")?;
2116
2117        assert_eq!(ast.contains_element("Menu"), true);
2118        let elt = ast.element("Menu").unwrap();
2119        assert_eq!(elt.len_conditional_blocks(), 1);
2120        let expected_dep = Dependency::new(&Expr::Sym("FOO".to_string()));
2121        let mut passes = 0;
2122        for block in elt.conditional_blocks() {
2123            assert_eq!(block.dependency(), expected_dep);
2124            passes += 1;
2125        }
2126        assert_eq!(passes, 1);
2127        Ok(())
2128    }
2129
2130    #[test]
2131    fn test_if_with_config() -> Result<(), Error> {
2132        let ast = ast_from(&"if CONDITION \n config FOO \n endif")?;
2133
2134        assert_eq!(ast.len_conditional_blocks(), 1);
2135        let expected_dep = Dependency::new(&Expr::Sym("CONDITION".to_string()));
2136        let mut passes = 0;
2137        for block in ast.conditional_blocks() {
2138            assert_eq!(block.dependency(), expected_dep);
2139            let sub_elements = elements_vec_to_hashmap(block.elements());
2140            assert_eq!(sub_elements.len(), 1);
2141            assert_eq!(sub_elements.contains_key("FOO"), true);
2142            let sub_elt = sub_elements.get("FOO").unwrap();
2143            assert_eq!(sub_elt.elt_type(), ElementType::Config);
2144            assert_eq!(sub_elt.name(), "FOO".to_string());
2145            passes += 1;
2146        }
2147        assert_eq!(passes, 1);
2148        Ok(())
2149    }
2150
2151    #[test]
2152    fn test_if_with_menuconfig() -> Result<(), Error> {
2153        let ast = ast_from(&"if CONDITION\n menuconfig FOO \n endif")?;
2154
2155        assert_eq!(ast.len_conditional_blocks(), 1);
2156        let expected_dep = Dependency::new(&Expr::Sym("CONDITION".to_string()));
2157        let mut passes = 0;
2158        for block in ast.conditional_blocks() {
2159            assert_eq!(block.dependency(), expected_dep);
2160            let sub_elements = elements_vec_to_hashmap(block.elements());
2161            assert_eq!(sub_elements.len(), 1);
2162            assert_eq!(sub_elements.contains_key("FOO"), true);
2163            let sub_elt = sub_elements.get("FOO").unwrap();
2164            assert_eq!(sub_elt.elt_type(), ElementType::MenuConfig);
2165            assert_eq!(sub_elt.name(), "FOO".to_string());
2166            passes += 1;
2167        }
2168        assert_eq!(passes, 1);
2169        Ok(())
2170    }
2171
2172    #[test]
2173    fn test_if_with_subif() -> Result<(), Error> {
2174        let ast = ast_from(&"if CONDITION if CONDITION2 endif endif")?;
2175
2176        assert_eq!(ast.len_conditional_blocks(), 1);
2177        let mut passes = 0;
2178        for block in ast.conditional_blocks() {
2179            assert_eq!(block.len_conditional_blocks(), 1);
2180            passes += 1;
2181        }
2182        assert_eq!(passes, 1);
2183        Ok(())
2184    }
2185
2186    #[test]
2187    fn test_nameless_mainmenu() -> Result<(), Error> {
2188        let ast = ast_from(&"")?;
2189        assert_eq!(ast.main_menu(), None);
2190        Ok(())
2191    }
2192}