1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use crate::errors::parser_error::ParserError;
use crate::parser::constants::{END_MENU_KEYWORD, MENU_KEYWORD, COMMENT_KEYWORD, IF_KEYWORD, SOURCE_KEYWORD, ENDIF_KEYWORD, CONFIG_KEYWORD, MENU_CONFIG_KEYWORD};
use crate::parser::kconfig_parser_impl::parser_traits::{Parseable, ParseableWithUnknownSpan, ParsingContext};
use crate::parser::kconfig_parser_impl::source_line_parser::parse_source_line;
use crate::parser::utils::find_index_of_next_end_keyword_in_context::find_index_of_next_end_keyword_in_context;
use crate::parser::utils::parse_span::ParseSpan;
use crate::parser::utils::tokenizer::LineKConfigTokenizerIterator;
use crate::structure::kconfig_node::KconfigNode;
use crate::structure::kconfig_node_children::KconfigNodeChildren;
use crate::structure::nodes::{KconfigConfigNode, KconfigIfNode, KconfigMenuConfigNode, KconfigMenuNode};

fn get_block_span<'a, 's, 'f>(
    context: &ParsingContext,
    keyword: &str,
    end_keyword: &str,
    end_span: &ParseSpan<'a, 's, 'f>,
) -> Option<ParseSpan<'a, 's, 'f>> {
    let end_at = find_index_of_next_end_keyword_in_context(keyword, end_keyword,
                                                           &context.with_different_span(
            &end_span,
        ),
    )?;

    Some(end_span.get_with_end_at(end_at))
}

impl Parseable for KconfigNodeChildren {
    fn parse(context: &ParsingContext) -> Result<Self, ParserError> {
        let span = context.span;
        let mut node_children = KconfigNodeChildren::new_empty();

        let mut line_index = 0;
        while line_index < span.len() {
            let line = &span.get_source_span()[line_index];
            let mut tokens = LineKConfigTokenizerIterator::from_line(line);

            if let Some(token) = tokens.next() {
                match token {
                    MENU_KEYWORD => {
                        let menu_span = get_block_span(context, MENU_KEYWORD, END_MENU_KEYWORD, &span.get_with_start_at(line_index))
                            .ok_or(ParserError::syntax_in_span_at("Expected a menu end keyword after a menu keyword.", &span, line_index))?;
                        let menu = KconfigMenuNode::parse(&context.with_different_span(
                            &menu_span,
                        ))?;
                        line_index += menu_span.len();
                        node_children.add_children(KconfigNode::Menu(menu));
                        continue;
                    },
                    IF_KEYWORD => {
                        let if_span = get_block_span(context, IF_KEYWORD, ENDIF_KEYWORD, &span.get_with_start_at(line_index))
                            .ok_or(ParserError::syntax_in_span_at("Expected a if end keyword after an if keyword.", &span, line_index))?;
                        let if_node = KconfigIfNode::parse(&context.with_different_span(
                            &if_span,
                        ))?;
                        line_index += if_span.len();
                        node_children.add_children(KconfigNode::If(if_node));
                        continue;
                    },
                    COMMENT_KEYWORD => {},
                    SOURCE_KEYWORD => {
                        let children_from_sourcing = parse_source_line(&context.get_line_context_with_span(
                            &span.get_line_span_at(line_index),
                        ))?;
                        node_children.add_all_children(children_from_sourcing);
                    },
                    CONFIG_KEYWORD => {
                        let config_potential_span = span.get_with_start_at(line_index);
                        let (config, config_span) = KconfigConfigNode::parse_with_unknown_span(
                            &context.with_different_span(&config_potential_span),
                        )?;

                        line_index += config_span.len();
                        node_children.add_children(KconfigNode::Config(config));
                        continue;
                    },
                    MENU_CONFIG_KEYWORD => {
                        let menu_config_potential_span = span.get_with_start_at(line_index);
                        let (menu_config, menu_config_span) = KconfigMenuConfigNode::parse_with_unknown_span(&context.with_different_span(
                            &menu_config_potential_span,
                        ))?;
                        line_index += menu_config_span.len();
                        node_children.add_children(KconfigNode::MenuConfig(menu_config));
                        continue;
                    },
                    _ => {}
                }
            }

            line_index += 1;
        }

        Ok(node_children)
    }
}