use crate::lex::parsing::ir::ParseNode;
use crate::lex::token::LineContainer;
use std::ops::Range;
mod builders;
pub(super) use builders::container_starts_with_pipe_row;
use builders::{
build_annotation_block, build_annotation_single, build_blank_line_group, build_definition,
build_list, build_paragraph, build_session, build_table, build_verbatim_block,
};
type ParserFn = dyn Fn(Vec<LineContainer>, &str) -> Result<Vec<ParseNode>, String>;
#[derive(Debug, Clone)]
pub(super) enum PatternMatch {
VerbatimBlock {
subject_idx: usize,
content_range: Range<usize>,
closing_idx: usize,
},
AnnotationBlock {
start_idx: usize,
content_idx: usize,
},
AnnotationSingle { start_idx: usize },
List {
items: Vec<(usize, Option<usize>)>,
preceding_blank_range: Option<Range<usize>>,
trailing_blank_range: Option<Range<usize>>,
},
Definition {
subject_idx: usize,
content_idx: usize,
},
Table {
subject_idx: usize,
content_idx: usize,
},
Session {
subject_idx: usize,
content_idx: usize,
preceding_blank_range: Option<Range<usize>>,
},
Paragraph { start_idx: usize, end_idx: usize },
BlankLineGroup,
DocumentTitle {
title_idx: usize,
subtitle_idx: Option<usize>,
},
DocumentStart,
}
pub(super) fn convert_pattern_to_node(
tokens: &[LineContainer],
pattern: &PatternMatch,
pattern_range: Range<usize>,
source: &str,
parse_children: &ParserFn,
) -> Result<ParseNode, String> {
let pattern_offset = pattern_range.start;
match pattern {
PatternMatch::VerbatimBlock {
subject_idx,
content_range,
closing_idx,
} => build_verbatim_block(tokens, *subject_idx, content_range.clone(), *closing_idx),
PatternMatch::AnnotationBlock {
start_idx,
content_idx,
} => build_annotation_block(
tokens,
pattern_offset + start_idx,
pattern_offset + content_idx,
source,
parse_children,
),
PatternMatch::AnnotationSingle { start_idx } => {
build_annotation_single(tokens, pattern_offset + start_idx)
}
PatternMatch::List { items, .. } => {
build_list(tokens, items, pattern_offset, source, parse_children)
}
PatternMatch::Definition {
subject_idx,
content_idx,
} => build_definition(
tokens,
pattern_offset + subject_idx,
pattern_offset + content_idx,
source,
parse_children,
),
PatternMatch::Table {
subject_idx,
content_idx,
} => build_table(
tokens,
pattern_offset + subject_idx,
pattern_offset + content_idx,
),
PatternMatch::Session {
subject_idx,
content_idx,
..
} => build_session(
tokens,
pattern_offset + subject_idx,
pattern_offset + content_idx,
source,
parse_children,
),
PatternMatch::Paragraph { start_idx, end_idx } => {
build_paragraph(tokens, pattern_offset + start_idx, pattern_offset + end_idx)
}
PatternMatch::BlankLineGroup => build_blank_line_group(tokens, pattern_range.clone()),
PatternMatch::DocumentTitle {
title_idx,
subtitle_idx,
} => build_document_title(
tokens,
pattern_offset + title_idx,
subtitle_idx.map(|i| pattern_offset + i),
),
PatternMatch::DocumentStart => build_document_start(),
}
}
fn build_document_title(
tokens: &[LineContainer],
title_idx: usize,
subtitle_idx: Option<usize>,
) -> Result<ParseNode, String> {
let mut title_tokens: Vec<(crate::lex::lexing::Token, std::ops::Range<usize>)> =
match &tokens[title_idx] {
LineContainer::Token(line) => line
.source_tokens
.iter()
.zip(line.token_spans.iter())
.map(|(token, span)| (token.clone(), span.clone()))
.collect(),
LineContainer::Container { .. } => {
return Err("Expected title token, found container".to_string())
}
};
let mut children = vec![];
if let Some(sub_idx) = subtitle_idx {
strip_trailing_colon(&mut title_tokens);
let subtitle_tokens = match &tokens[sub_idx] {
LineContainer::Token(line) => line
.source_tokens
.iter()
.zip(line.token_spans.iter())
.map(|(token, span)| (token.clone(), span.clone()))
.collect(),
LineContainer::Container { .. } => {
return Err("Expected subtitle token, found container".to_string())
}
};
children.push(ParseNode::new(
crate::lex::parsing::ir::NodeType::DocumentSubtitle,
subtitle_tokens,
vec![],
));
}
Ok(ParseNode::new(
crate::lex::parsing::ir::NodeType::DocumentTitle,
title_tokens,
children,
))
}
fn strip_trailing_colon(tokens: &mut Vec<(crate::lex::lexing::Token, std::ops::Range<usize>)>) {
use crate::lex::lexing::Token;
if tokens.is_empty() {
return;
}
let mut idx = tokens.len();
while idx > 0 {
idx -= 1;
match &tokens[idx].0 {
Token::BlankLine(_) | Token::Whitespace(_) => continue,
Token::Colon => {
tokens.remove(idx);
return;
}
_ => return,
}
}
}
fn build_document_start() -> Result<ParseNode, String> {
Ok(ParseNode::new(
crate::lex::parsing::ir::NodeType::DocumentStart,
vec![],
vec![],
))
}
pub(super) fn blank_line_node_from_range(
tokens: &[LineContainer],
token_range: Range<usize>,
) -> Result<ParseNode, String> {
build_blank_line_group(tokens, token_range)
}