rustla/parser/state_machine/
aplus_questionnaire.rs

1/*!
2A submodule dedicated to parsing functions of the `State::AplusQuestionnaire` state.
3
4Copyright © 2020 Santtu Söderholm
5*/
6
7use super::*;
8
9/// A function for reading in blocks of intermediate text (rST paragraphs) between questionnaire questions.
10pub fn parse_aplus_questionnaire_text(
11    src_lines: &Vec<String>,
12    base_indent: usize,
13    section_level: &mut usize,
14    line_cursor: &mut LineCursor,
15    mut doctree: DocTree,
16    captures: &regex::Captures,
17    pattern_name: &Pattern,
18) -> TransitionResult {
19
20    let detected_indent = captures.get(1).unwrap().as_str().chars().count();
21
22    match Parser::parent_indent_matches(doctree.shared_data(), detected_indent) {
23        IndentationMatch::JustRight => {
24            let start_line = line_cursor.relative_offset();
25            let indent_allowed = true;
26            let remove_indent = true;
27            let alignment = Some(detected_indent);
28            let (block_lines, offset) = if let TextBlockResult::Ok {lines, offset } = Parser::read_text_block(
29                src_lines,
30                start_line,
31                indent_allowed,
32                remove_indent,
33                alignment,
34                true
35            ) {
36                (lines, offset)
37            } else {
38                panic!("Error when reading intermediate text in A+ questionnaire on line {}. Computer says no...", line_cursor.sum_total())
39            };
40
41            let inline_nodes = match Parser::inline_parse(block_lines.join("\n"), None, line_cursor) {
42                InlineParsingResult::Nodes(nodes) => nodes,
43                _ => panic!(
44                    "Could not parse intermediate questionnaire text on line {} for inline nodes. Computer says no...",
45                    line_cursor.sum_total()
46                )
47            };
48
49            let paragraph = TreeNodeType::Paragraph {
50                indent: detected_indent,
51            };
52            doctree = match doctree.push_data_and_focus(paragraph) {
53                Ok(tree) => tree,
54                Err(tree) => {
55                    return TransitionResult::Failure {
56                        message: format!(
57                            "Node insertion error on line {}. Computer says no...",
58                            line_cursor.sum_total()
59                        ),
60                        doctree: tree,
61                    }
62                }
63            };
64            for node in inline_nodes {
65                doctree = match doctree.push_data(node) {
66                    Ok(tree) => tree,
67                    Err(tree) => {
68                        return TransitionResult::Failure {
69                            message: format!(
70                                "Node insertion error on line {}. Computer says no...",
71                                line_cursor.sum_total()
72                            ),
73                            doctree: tree,
74                        }
75                    }
76                };
77            }
78            doctree = doctree.focus_on_parent();
79            return TransitionResult::Success {
80                doctree: doctree,
81                push_or_pop: PushOrPop::Neither,
82                line_advance: LineAdvance::Some(1),
83            };
84        }
85        _ => {
86            doctree = doctree.focus_on_parent();
87            return TransitionResult::Success {
88                doctree: doctree,
89                push_or_pop: PushOrPop::Pop,
90                line_advance: LineAdvance::None,
91            };
92        }
93    }
94}
95
96/// Parses an A+ questionnaire directive. Works as an entry point between the parser and the functions
97/// `crate::parser::directive_parsers::{parse_aplus_pick_any, parse_aplus_pick_one, parse_aplus_freetext}`.
98pub fn parse_aplus_questionnaire_directive(
99    src_lines: &Vec<String>,
100    base_indent: usize,
101    section_level: &mut usize,
102    line_cursor: &mut LineCursor,
103    mut doctree: DocTree,
104    captures: &regex::Captures,
105    pattern_name: &Pattern,
106) -> TransitionResult {
107
108    let detected_marker_indent = captures.get(1).unwrap().as_str().chars().count() + base_indent;
109    let detected_directive_label = captures
110        .get(2)
111        .unwrap()
112        .as_str()
113        .split_whitespace()
114        .collect::<String>()
115        .to_lowercase();
116    let detected_first_indent = captures.get(0).unwrap().as_str().chars().count() + base_indent;
117
118    let empty_after_marker: bool = {
119        let line = src_lines.get(line_cursor.relative_offset()).unwrap(); // Unwrapping is not a problem here.
120
121        match line.char_indices().nth(detected_first_indent) {
122            Some((index, _)) => line[index..].trim().is_empty(),
123            None => true,
124        }
125    };
126
127    let (body_indent, body_offset) =
128        match Parser::indent_on_subsequent_lines(src_lines, line_cursor.relative_offset() + 1) {
129            Some((indent, offset)) => (indent + base_indent, offset),
130            None => (detected_first_indent, 0), // EOF encountered => stay on same line
131        };
132
133    match Parser::parent_indent_matches(doctree.shared_data(), detected_marker_indent) {
134        IndentationMatch::JustRight => match detected_directive_label.as_str() {
135            "pick-one" => directive_parsers::parse_aplus_pick_one(
136                src_lines,
137                doctree,
138                line_cursor,
139                detected_first_indent,
140                body_indent,
141                empty_after_marker,
142            ),
143            "pick-any" => directive_parsers::parse_aplus_pick_any(
144                src_lines,
145                doctree,
146                line_cursor,
147                detected_first_indent,
148                body_indent,
149                empty_after_marker,
150            ),
151            "freetext" => directive_parsers::parse_aplus_freetext(
152                src_lines,
153                doctree,
154                line_cursor,
155                detected_first_indent,
156                body_indent,
157                empty_after_marker,
158            ),
159            _ => {
160                doctree = doctree.focus_on_parent();
161                return TransitionResult::Success {
162                    doctree: doctree,
163                    push_or_pop: PushOrPop::Pop,
164                    line_advance: LineAdvance::None,
165                };
166            }
167        },
168        _ => {
169            doctree = doctree.focus_on_parent();
170            return TransitionResult::Success {
171                doctree: doctree,
172                push_or_pop: PushOrPop::Pop,
173                line_advance: LineAdvance::None,
174            };
175        }
176    }
177}