1use std::cmp;
14
15use std::collections;
16use std::str;
17
18use regex::Regex;
21
22use super::*;
26use crate::parser::types_and_aliases::IndentedBlockResult;
27
28
29pub mod automata;
30mod regex_patterns;
31
32mod converters;
33
34pub mod types_and_aliases;
35use types_and_aliases::*;
36
37pub mod line_cursor;
38use line_cursor::{Line, LineCursor};
39
40pub mod state_machine;
41use state_machine::{State, COMPILED_INLINE_TRANSITIONS};
42
43pub mod directive_parsers;
44mod table_parsers;
45
46use crate::common::{
47 EnumDelims, EnumKind, FootnoteKind, ParsingResult, SectionLineStyle,
48};
49use crate::parser::regex_patterns::Pattern;
50use crate::doctree::tree_node::TreeNode;
51use crate::doctree::tree_node_types::TreeNodeType;
52use crate::doctree::DocTree;
53
54mod tests;
55
56pub struct Parser {
67
68 src_lines: Vec<String>,
70
71 line_cursor: LineCursor,
73
74 base_indent: usize,
79
80 section_level: usize,
83
84 doctree: Option<DocTree>,
88
89 state_stack: Vec<State>,
92}
93
94impl Parser {
95 pub fn new(
101 src: Vec<String>,
102 doctree: DocTree,
103 base_indent: usize,
104 base_line: Line,
105 initial_state: State,
106 section_level: usize,
107 ) -> Self {
108 Self {
109 src_lines: src, line_cursor: LineCursor::new(0, base_line),
111 base_indent: base_indent,
112 section_level: section_level,
113 doctree: Some(doctree),
114 state_stack: vec![initial_state],
115 }
116 }
117
118 pub fn parse(&mut self) -> ParsingResult {
121 let mut line_changed: bool = false;
126 let mut line_not_changed_count: u32 = 0;
127
128 loop {
130 if ! line_changed && line_not_changed_count >= 10 {
135 return ParsingResult::Failure {
136 message: format!("Line not advanced even after {} iterations of the parsing loop on line {}. Clearly something is amiss...", line_not_changed_count, self.line_cursor.sum_total()),
137 doctree: if let Some(doctree) = self.doctree.take() {
138 doctree
139 } else {
140 panic!(
141 "Doctree lost during parsing process around line {}. Computer says no...",
142 self.line_cursor.sum_total()
143 )
144 }
145 };
146 }
147
148 line_changed = false;
149
150 let mut match_found = false;
151
152 let latest_state_transitions = if let Some(machine) = self.state_stack.last() {
157 match machine {
158 State::EOF => {
159 match self.doctree.take() {
160 Some(doctree) => {
161 return ParsingResult::EOF {
162 doctree: doctree,
163 state_stack: self
164 .state_stack
165 .drain(..self.state_stack.len() - 1)
166 .collect(),
167 }
168 }
169 None => {
170 panic!("Tree should not be in the possession of a transition method after moving past EOF...")
171 }
172 };
173 }
174
175 State::Failure { .. } => {
176 return ParsingResult::Failure {
177 message: String::from("Parsing ended in Failure state...\n"),
178 doctree: if let Some(doctree) = self.doctree.take() {
179 doctree
180 } else {
181 panic!("Lost doctree inside parsing function before line {}. Computer says no...", self.line_cursor.sum_total())
182 },
183 }
184 }
185
186 _ => {
187 if let Ok(transitions_ref) = machine.get_transitions(&self.line_cursor) {
188 transitions_ref
189 } else {
190 return ParsingResult::Failure {
191 message: String::from("No transitions for this state...\n"),
192 doctree: if let Some(doctree) = self.doctree.take() {
193 doctree
194 } else {
195 panic!("Lost doctree inside parsing function before line {}. Computer says no...", self.line_cursor.sum_total())
196 },
197 };
198 }
199 }
200 }
201 } else {
202 return ParsingResult::EmptyStateStack {
203 state_stack: Vec::new(),
204 doctree: if let Some(doctree) = self.doctree.take() {
205 doctree
206 } else {
207 panic!("Doctree lost during parsing process around line {}. Computer says no...", self.line_cursor.sum_total())
208 },
209 };
210 };
211
212 for (pattern_name, regex, method) in latest_state_transitions.iter() {
214 let src_line: &str = match Parser::get_source_from_line(&self.src_lines, self.line_cursor.relative_offset()) {
216 Some(line) => line,
217 None => {
218 return ParsingResult::Failure {
219 message: String::from("Parsing ended prematurely because of an unqualified move past EOF..."),
220 doctree: if let Some(doctree) = self.doctree.take() { doctree } else {
221 panic!(
222 "Lost doctree inside parsing function before line {}. Computer says no...",
223 self.line_cursor.sum_total()
224 )
225 }
226 }
227 }
228 };
229
230 if let Some(captures) = regex.captures(src_line) {
232 match_found = true;
235
236 let line_before_transition = self.line_cursor.sum_total();
240
241 let doctree = if let Some(doctree) = self.doctree.take() {
242 doctree
243 } else {
244 panic!(
245 "Doctree lost inside transition function around line {}? Computer says no...",
246 self.line_cursor.sum_total()
247 )
248 };
249 self.doctree = match method(
250 &self.src_lines,
251 self.base_indent,
252 &mut self.section_level,
253 &mut self.line_cursor,
254 doctree,
255 &captures,
256 pattern_name,
257 ) {
258 TransitionResult::Success {
259 doctree,
260 push_or_pop,
261 line_advance,
262 } => {
263 match push_or_pop {
264 PushOrPop::Push(mut states) => {
265 self.state_stack.append(&mut states);
266 }
267 PushOrPop::Pop => {
268 match self.state_stack.pop() {
269 Some(machine) => (),
270 None => {
271 return ParsingResult::Failure {
272 message: String::from(
273 "Can't pop from empty stack...\n",
274 ),
275 doctree: if let Some(doctree) = self.doctree.take() {
276 doctree
277 } else {
278 panic!("Lost doctree inside parsing function before line {}. Computer says no...", self.line_cursor.sum_total())
279 },
280 }
281 }
282 };
283 }
284
285 PushOrPop::Neither => {} };
287
288 if let LineAdvance::Some(offset) = line_advance {
289 self.line_cursor.increment_by(offset);
290 }
291
292 if self.line_cursor.sum_total() == line_before_transition {
294 line_not_changed_count += 1;
295 } else {
296 line_changed = true;
297 line_not_changed_count = 0;
298 }
299
300 Some(doctree)
301 }
302
303 TransitionResult::Failure { message, doctree } => {
304 return ParsingResult::Failure {
305 message: message,
306 doctree: doctree,
307 }
308 }
309 };
310
311 break; }
313 }
314
315 if ! match_found {
318 if let None = self.state_stack.pop() {
319 return ParsingResult::EmptyStateStack {
320 doctree: self.doctree.take().unwrap(),
321 state_stack: self.state_stack.drain(..self.state_stack.len()).collect(),
322 };
323 };
324 if let Some(doctree) = self.doctree.take() {
325 self.doctree = Some(doctree.focus_on_parent());
326 } else {
327 return ParsingResult::Failure {
328 message: format!(
329 "Doctree in possession of transition method after transition on line {}. Computer says no...",
330 self.line_cursor.sum_total()
331 ),
332 doctree: if let Some(doctree) = self.doctree.take() { doctree } else {
333 panic!(
334 "Lost doctree inside parsing function before line {}. Computer says no...",
335 self.line_cursor.sum_total()
336 )
337 }
338 };
339 }
340 }
341
342 if self.line_cursor.relative_offset() >= self.src_lines.len() {
343 self.state_stack.push(State::EOF);
344 }
345 }
346 }
347
348 fn jump_to_line(&mut self, line: usize) -> Result<(), &'static str> {
351 if line < self.src_lines.len() {
352 *self.line_cursor.relative_offset_mut_ref() = line;
353 } else {
354 return Err("Attempted a move to a non-existent line.\nComputer says no...\n");
355 }
356
357 Ok(())
358 }
359
360 fn nth_next_line(&mut self, n: usize) -> Result<(), &'static str> {
364 *self.line_cursor.relative_offset_mut_ref() =
365 match self.line_cursor.relative_offset().checked_add(n) {
366 Some(value) => value,
367 None => {
368 return Err("Attempted indexing with integer overflow.\nComputer says no...\n")
369 }
370 };
371
372 if self.line_cursor.relative_offset() > self.src_lines.len() {
373 return Err("No such line number.\nComputer says no...\n");
374 }
375
376 Ok(())
377 }
378
379 fn nth_previous_line(&mut self, n: usize) -> Result<(), &'static str> {
383 *self.line_cursor.relative_offset_mut_ref() =
384 match self.line_cursor.relative_offset().checked_sub(n) {
385 Some(value) => value,
386 None => {
387 return Err("Attempted indexing with integer overflow. Computer says no...")
388 }
389 };
390
391 if self.line_cursor.relative_offset() > self.src_lines.len() {
392 return Err("No such line number. Computer says no...");
393 }
394
395 Ok(())
396 }
397
398 fn get_source_from_line<'src_lines>(src_lines: &Vec<String>, line_num: usize) -> Option<&str> {
402 let src = match src_lines.get(line_num) {
403 Some(line) => line.as_str(),
404 None => {
405 eprintln!(
406 "No such line number ({} out of bounds). Computer says no...",
407 line_num
408 );
409 return None;
410 }
411 };
412
413 Some(src)
414 }
415
416 fn inline_parse(
419 inline_src_block: String,
420 mut doctree: Option<&mut DocTree>,
421 line_cursor: &mut LineCursor,
422 ) -> InlineParsingResult {
423 let mut nodes_data: Vec<TreeNodeType> = Vec::new();
424
425 let mut col: usize = 0;
426
427 let src_chars = &mut inline_src_block.chars();
428
429 loop {
430 match Parser::match_inline_str(&mut doctree, &src_chars) {
431 Some((mut node_data, offset)) => {
432 nodes_data.append(&mut node_data);
433
434 for _ in 0..offset {
436 if let Some(c) = src_chars.next() {
437 col += 1;
438 if c == '\n' {
439 line_cursor.increment_by(1);
440 col = 0;
441 }
442 } else {
443 break;
444 }
445 }
446 }
447
448 None => break,
452 }
453 }
454
455 if nodes_data.is_empty() {
456 return InlineParsingResult::NoNodes;
457 } else {
458 return InlineParsingResult::Nodes(nodes_data);
459 }
460 }
461
462 fn match_inline_str<'chars>(
467 opt_doctree_ref: &mut Option<&mut DocTree>,
468 chars_iter: &'chars str::Chars,
469 ) -> Option<(Vec<TreeNodeType>, usize)> {
470 let src_str = chars_iter.as_str();
471 if src_str.is_empty() {
472 return None;
473 }
474 for (pattern_name, regexp, parsing_function) in COMPILED_INLINE_TRANSITIONS.iter() {
475 match regexp.captures(src_str) {
476 Some(capts) => {
477 let (node_type, offset) =
478 parsing_function(opt_doctree_ref, *pattern_name, &capts);
479 return Some((node_type, offset));
480 }
481 None => continue, };
483 }
484 None
485 }
486
487 fn parse_first_node_block(
490 doctree: DocTree,
491 src_lines: &Vec<String>,
492 base_indent: usize,
493 line_cursor: &mut LineCursor,
494 text_indent: usize,
495 first_indent: Option<usize>,
496 start_state: State,
497 section_level: &mut usize,
498 force_alignment: bool,
499 ) -> Result<(ParsingResult, usize), ParsingResult> {
500 let relative_first_indent = first_indent.unwrap_or(text_indent) - base_indent;
501 let relative_block_indent = text_indent - base_indent;
502
503 let (block, line_offset) = match Parser::read_indented_block(
505 src_lines,
506 line_cursor.relative_offset(),
507 true,
508 true,
509 Some(relative_block_indent),
510 Some(relative_first_indent),
511 force_alignment,
512 ) {
513 IndentedBlockResult::Ok {lines, minimum_indent, offset, blank_finish } => (lines, offset),
514 _ => {
515 return Err(ParsingResult::Failure {
516 message: format!(
517 "Error when reading in a block of text for nested parse in line {}.",
518 line_cursor.sum_total()
519 ),
520 doctree: doctree,
521 })
522 }
523 };
524
525 match Parser::new(
527 block,
528 doctree,
529 text_indent,
530 line_cursor.sum_total(),
531 start_state,
532 *section_level,
533 ).parse() {
534 ParsingResult::EOF {
535 doctree,
536 state_stack,
537 } => {
538 return Ok((
539 ParsingResult::EOF {
540 doctree: doctree,
541 state_stack: state_stack,
542 },
543 line_offset,
544 ))
545 }
546 ParsingResult::EmptyStateStack {
547 doctree,
548 state_stack,
549 } => {
550 return Ok((
551 ParsingResult::EmptyStateStack {
552 doctree: doctree,
553 state_stack: state_stack,
554 },
555 line_offset,
556 ))
557 }
558 ParsingResult::Failure { message, doctree } => {
559 return Err(ParsingResult::Failure {
560 message: format!("Nested parse ended in failure: {}", message),
561 doctree: doctree,
562 })
563 }
564 };
565 }
566
567 fn skip_to_next_block(src_lines: &Vec<String>, line_cursor: &mut LineCursor) -> Option<()> {
570 loop {
571 if let Some(line) = src_lines.get(line_cursor.relative_offset()) {
572 if line.trim().is_empty() {
573 line_cursor.increment_by(1);
574 } else {
575 break Some(())
576 }
577 } else {
578 break None
579 }
580 }
581 }
582
583 fn read_text_block(
590 src_lines: &Vec<String>,
591 start_line: usize,
592 indent_allowed: bool,
593 remove_indent: bool,
594 alignment: Option<usize>,
595 until_blank: bool
596 ) -> TextBlockResult {
597
598 let mut line_num = start_line;
599 let last_line = src_lines.len();
600 let mut lines: Vec<String> = Vec::with_capacity(last_line - start_line);
601
602 while line_num < last_line {
603 let mut line: String = match src_lines.get(line_num) {
604 Some(line) => line.clone(),
605 None => return TextBlockResult::Err {
606 offset: {
607 lines.shrink_to_fit();
608 lines.len()
609 },
610 lines: lines,
611 }
612 };
613
614 if line.trim().is_empty() {
615 if until_blank {
616 break
617 } else {
618 lines.push(line.trim().to_string());
619 line_num += 1;
620 continue
621 }
622 }
623
624 let line_indent = line
625 .as_str()
626 .chars()
627 .take_while(|c| c.is_whitespace())
628 .count();
629
630 if !indent_allowed && line_indent > 0 {
631 break;
632 }
633
634 if let Some(alignment) = alignment {
635 if alignment != line_indent {
636 break;
637 }
638 }
639
640 if remove_indent {
641 line = line.as_str().trim_start().to_string();
642 }
643
644 lines.push(line);
645 line_num += 1;
646 }
647
648 lines.shrink_to_fit();
649 let offset = lines.len();
650 TextBlockResult::Ok {
651 lines: lines,
652 offset: offset
653 }
654 }
655
656 fn read_indented_block(
660 src_lines: &Vec<String>,
661 start_line: usize,
662 until_blank: bool,
663 strip_indent: bool,
664 block_indent: Option<usize>,
665 first_indent: Option<usize>,
666 force_alignment: bool,
667 ) -> IndentedBlockResult {
668
669 if src_lines.is_empty() {
670 return IndentedBlockResult::EmptyLinesErr
671 }
672
673 let mut line_num = start_line;
674 let last_line_num = src_lines.len();
675
676 let mut block_lines: Vec<String> = Vec::with_capacity(last_line_num - start_line);
677
678 let mut minimal_indent = match block_indent {
680 Some(indent) => Some(indent),
681 None => None,
682 };
683
684 let first_indent = if let (Some(block_indent), None) = (block_indent, first_indent) {
687 Some(block_indent)
688 } else {
689 first_indent
690 };
691
692 if first_indent.is_some() {
695 let line = src_lines.get(line_num).unwrap().to_owned();
696 block_lines.push(line);
697 line_num += 1;
698 }
699
700 let mut blank_finish: bool = false;
701 let mut loop_broken: bool = false; while line_num < last_line_num {
704 let line: String = match src_lines.get(line_num) {
705 Some(line) => line.clone(),
706 None => {
707 loop_broken = true;
708 break
709 }
710 };
711
712 let line_is_empty = line.trim().is_empty();
713
714 let line_indent = line
716 .as_str()
717 .chars()
718 .take_while(|c| c.is_whitespace())
719 .count();
720
721 let break_when_not_aligned: bool = if block_indent.is_some() && force_alignment {
722 line_indent != block_indent.unwrap()
723 } else if block_indent.is_some() {
724 line_indent < block_indent.unwrap()
725 } else {
726 false
727 };
728
729 if !line_is_empty && (line_indent < 1 || break_when_not_aligned) {
730 blank_finish = (line_num > start_line)
732 && src_lines.get(line_num - 1).unwrap().trim().is_empty();
733 loop_broken = true;
734 break;
735 }
736
737 if line_is_empty && until_blank {
740 blank_finish = true;
741 break;
742 } else if block_indent.is_none() {
743 if minimal_indent.is_none() {
744 minimal_indent = Some(line_indent);
745 } else if line_indent > 0 {
746 minimal_indent = Some(cmp::min(minimal_indent.unwrap(), line_indent));
747 }
748 }
749
750 block_lines.push(line);
751 line_num += 1;
752 }
753
754 if !loop_broken {
755 blank_finish = true;
756 } if let Some(min_indent) = minimal_indent {
760 if strip_indent {
761 for (index, line) in block_lines.iter_mut().enumerate() {
762 let indent = if first_indent.is_some() && index == 0 {
763 first_indent.unwrap()
764 } else {
765 min_indent
766 };
767 *line = line.chars().skip(indent).collect::<String>();
768 }
769 }
770 }
771
772 block_lines.shrink_to_fit(); let line_diff = block_lines.len();
774
775 IndentedBlockResult::Ok {
776 lines: block_lines,
777 minimum_indent: minimal_indent.unwrap(),
778 offset: line_diff,
779 blank_finish: blank_finish
780 }
781 }
782
783 fn parent_indent_matches(
785 parent: &TreeNodeType,
786 relevant_child_indent: usize,
787 ) -> IndentationMatch {
788 if let Some(indent) = parent.body_indent() {
789 if indent > relevant_child_indent {
790 IndentationMatch::TooLittle
791 } else if indent == relevant_child_indent {
792 IndentationMatch::JustRight
793 } else {
794 IndentationMatch::TooMuch
795 }
796 } else {
797 panic!("Asked for parent indentation inside a \"{}\" that is not a container. Computer says no...", parent)
798 }
799 }
800
801 fn indent_on_subsequent_lines(
803 src_lines: &Vec<String>,
804 mut current_line: usize,
805 ) -> Option<(usize, usize)> {
806 loop {
807 if let Some(line) = src_lines.get(current_line + 1) {
808 if line.trim().is_empty() {
809 current_line += 1;
810 continue;
811 } else {
812 break Some((
813 line.chars().take_while(|c| c.is_whitespace()).count(),
814 current_line - current_line,
815 ));
816 }
817 } else {
818 break None;
819 }
820 }
821 }
822
823 fn line_prefix(line: &str, end_index: usize) -> String {
825 let prefix = line
826 .chars()
827 .enumerate()
828 .take_while(|(i, c)| *c == '\n' || *i < end_index)
829 .map(|(i, c)| c)
830 .collect::<String>();
831
832 if prefix.chars().count() < end_index {
833 eprintln!("Encountered a newline or line shorter than given...")
834 }
835
836 prefix
837 }
838
839 fn line_suffix(line: &str, start_index: usize) -> String {
842 let suffix_len = match line.chars().count().checked_sub(start_index) {
843 Some(len) => len,
844 None => {
845 panic!("Cannot scan line suffix whose start index is greater than the line length")
846 }
847 };
848
849 let suffix = line
850 .chars()
851 .enumerate()
852 .skip_while(|(i, c)| *c == '\n' || *i < start_index)
853 .map(|(i, c)| c)
854 .collect::<String>();
855
856 if suffix.chars().count() > suffix_len {
857 eprintln!("Encountered a newline before reaching suffix. Returning an empty string...");
858 String::new()
859 } else {
860 suffix
861 }
862 }
863
864 fn skip_empty_lines(src_lines: &Vec<String>, line_cursor: &mut LineCursor) -> usize {
867 let mut lines_skipped = 0 as usize;
868
869 while let Some(line) = src_lines.get(line_cursor.relative_offset()) {
870 if line.trim().is_empty() {
871 line_cursor.increment_by(1); lines_skipped += 1;
873 } else {
874 break;
875 }
876 }
877
878 lines_skipped
879 }
880
881 fn indent_from_next_line (
890 src_lines: &Vec<String>,
891 base_indent: usize,
892 marker_indent: usize,
893 indent_after_marker: usize,
894 line_cursor: &LineCursor
895 ) -> usize {
896 match src_lines.get(line_cursor.relative_offset() + 1) {
897 Some(line) => if line.trim().is_empty() {
898 indent_after_marker
899 } else {
900 let indent = line
901 .chars()
902 .take_while(|c| c.is_whitespace())
903 .count() + base_indent;
904 if indent < marker_indent + 1 {
905 indent_after_marker
906 } else {
907 indent
908 }
909 }
910 None => indent_after_marker
911 }
912 }
913}