use crate::lex::ast::elements::typed_content::{ContentElement, SessionContent};
use crate::lex::ast::{Data, ListItem};
use crate::lex::parsing::ContentItem;
use crate::lex::token::{normalization, LineToken};
use super::ast_nodes;
use super::extraction;
use crate::lex::ast::range::SourceLocation;
pub fn paragraph_from_line_tokens(
line_tokens: &[LineToken],
source: &str,
source_location: &SourceLocation,
) -> ContentItem {
let token_lines = normalization::normalize_line_tokens(line_tokens);
let data = extraction::extract_paragraph_data(token_lines, source);
ast_nodes::paragraph_node(data, source_location)
}
pub fn session_from_title_token(
title_token: &LineToken,
content: Vec<SessionContent>,
source: &str,
source_location: &SourceLocation,
) -> ContentItem {
let tokens = normalization::normalize_line_token(title_token);
let data = extraction::extract_session_data(tokens, source);
ast_nodes::session_node(data, content, source_location)
}
pub fn definition_from_subject_token(
subject_token: &LineToken,
content: Vec<ContentElement>,
source: &str,
source_location: &SourceLocation,
) -> ContentItem {
let tokens = normalization::normalize_line_token(subject_token);
let data = extraction::extract_definition_data(tokens, source);
ast_nodes::definition_node(data, content, source_location)
}
pub fn list_from_items(items: Vec<ListItem>) -> ContentItem {
ast_nodes::list_node(items)
}
pub fn list_item_from_marker_token(
marker_token: &LineToken,
content: Vec<ContentElement>,
source: &str,
source_location: &SourceLocation,
) -> ListItem {
let tokens = normalization::normalize_line_token(marker_token);
let data = extraction::extract_list_item_data(tokens, source);
ast_nodes::list_item_node(data, content, source_location)
}
pub fn annotation_from_label_token(
label_token: &LineToken,
content: Vec<ContentElement>,
source: &str,
source_location: &SourceLocation,
) -> ContentItem {
let tokens = normalization::normalize_line_token(label_token);
let data = data_from_tokens(tokens, source, source_location);
ast_nodes::annotation_node(data, content)
}
pub fn data_from_tokens(
label_tokens: Vec<(Token, ByteRange<usize>)>,
source: &str,
source_location: &SourceLocation,
) -> Data {
let data = extraction::extract_data(label_tokens, source);
ast_nodes::data_node(data, source_location)
}
pub fn verbatim_block_from_lines(
subject_token: &LineToken,
content_tokens: &[LineToken],
closing_data: Data,
source: &str,
source_location: &SourceLocation,
) -> ContentItem {
let data = extraction::extract_verbatim_block_data(subject_token, content_tokens, source);
ast_nodes::verbatim_block_node(data, closing_data, source_location)
}
pub fn table_from_lines(
subject_token: &LineToken,
content_tokens: &[LineToken],
config_annotation: Option<ContentItem>,
source: &str,
source_location: &SourceLocation,
) -> ContentItem {
let data = extraction::extract_table_data(subject_token, content_tokens, source);
let alignments: Vec<crate::lex::ast::TableCellAlignment> = Vec::new();
let mut table_item = ast_nodes::table_node(data, &alignments, source_location);
if let Some(ContentItem::Annotation(annotation)) = config_annotation {
if let ContentItem::Table(ref mut table) = table_item {
table.annotations.push(annotation);
}
}
table_item
}
use crate::lex::token::Token;
use std::ops::Range as ByteRange;
pub fn paragraph_from_token_lines(
mut token_lines: Vec<Vec<(Token, ByteRange<usize>)>>,
source: &str,
source_location: &SourceLocation,
) -> ContentItem {
if token_lines.len() == 1 {
let mut new_token_lines = vec![];
let mut current_line = vec![];
for token_data in token_lines.remove(0) {
if let Token::BlankLine(_) = token_data.0 {
if !current_line.is_empty() {
new_token_lines.push(current_line);
current_line = vec![];
}
} else {
current_line.push(token_data);
}
}
if !current_line.is_empty() {
new_token_lines.push(current_line);
}
token_lines = new_token_lines;
}
let data = extraction::extract_paragraph_data(token_lines, source);
ast_nodes::paragraph_node(data, source_location)
}
pub fn session_from_tokens(
title_tokens: Vec<(Token, ByteRange<usize>)>,
content: Vec<SessionContent>,
source: &str,
source_location: &SourceLocation,
) -> ContentItem {
let filtered_tokens: Vec<_> = title_tokens
.into_iter()
.filter(|(token, _)| !matches!(token, Token::BlankLine(_)))
.collect();
let data = extraction::extract_session_data(filtered_tokens, source);
ast_nodes::session_node(data, content, source_location)
}
pub fn definition_from_tokens(
subject_tokens: Vec<(Token, ByteRange<usize>)>,
content: Vec<ContentElement>,
source: &str,
source_location: &SourceLocation,
) -> ContentItem {
let filtered_tokens: Vec<_> = subject_tokens
.into_iter()
.filter(|(token, _)| !matches!(token, Token::BlankLine(_)))
.collect();
let data = extraction::extract_definition_data(filtered_tokens, source);
ast_nodes::definition_node(data, content, source_location)
}
pub fn list_item_from_tokens(
marker_tokens: Vec<(Token, ByteRange<usize>)>,
content: Vec<ContentElement>,
source: &str,
source_location: &SourceLocation,
) -> ListItem {
let data = extraction::extract_list_item_data(marker_tokens, source);
ast_nodes::list_item_node(data, content, source_location)
}
pub fn annotation_from_tokens(
label_tokens: Vec<(Token, ByteRange<usize>)>,
content: Vec<ContentElement>,
source: &str,
source_location: &SourceLocation,
) -> ContentItem {
let data = data_from_tokens(label_tokens, source, source_location);
ast_nodes::annotation_node(data, content)
}
pub fn blank_line_group_from_tokens(
tokens: Vec<(Token, ByteRange<usize>)>,
_source: &str,
source_location: &SourceLocation,
) -> ContentItem {
ast_nodes::blank_line_group_node(tokens, source_location)
}
pub fn text_content_from_tokens(
tokens: Vec<(Token, ByteRange<usize>)>,
source: &str,
source_location: &SourceLocation,
) -> crate::lex::ast::text_content::TextContent {
use crate::lex::token::normalization::utilities::{compute_bounding_box, extract_text};
let filtered_tokens: Vec<_> = tokens
.into_iter()
.filter(|(token, _)| !matches!(token, Token::BlankLine(_)))
.collect();
if filtered_tokens.is_empty() {
return crate::lex::ast::text_content::TextContent::from_string(String::new(), None);
}
let byte_range = compute_bounding_box(&filtered_tokens);
let text = extract_text(byte_range.clone(), source);
let location = source_location.byte_range_to_ast_range(&byte_range);
crate::lex::ast::text_content::TextContent::from_string(text, Some(location))
}
pub fn paragraph_from_text_segments(
text_lines: Vec<(String, crate::lex::ast::Range)>,
overall_location: crate::lex::ast::Range,
) -> ContentItem {
use crate::lex::ast::{Paragraph, TextContent, TextLine};
let lines: Vec<ContentItem> = text_lines
.into_iter()
.map(|(text, location)| {
let text_content = TextContent::from_string(text, Some(location.clone()));
let text_line = TextLine::new(text_content).at(location);
ContentItem::TextLine(text_line)
})
.collect();
ContentItem::Paragraph(Paragraph {
lines,
annotations: Vec::new(),
location: overall_location,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lex::ast::elements::typed_content::SessionContent;
use crate::lex::ast::range::SourceLocation;
use crate::lex::token::LineType;
use crate::lex::token::Token;
fn make_line_token(tokens: Vec<Token>, spans: Vec<std::ops::Range<usize>>) -> LineToken {
LineToken {
source_tokens: tokens,
token_spans: spans,
line_type: LineType::ParagraphLine,
}
}
#[test]
fn test_paragraph_from_line_tokens() {
let source = "hello world";
let source_location = SourceLocation::new(source);
let line_tokens = vec![make_line_token(
vec![
Token::Text("hello".to_string()),
Token::Whitespace(1),
Token::Text("world".to_string()),
],
vec![0..5, 5..6, 6..11],
)];
let result = paragraph_from_line_tokens(&line_tokens, source, &source_location);
match result {
ContentItem::Paragraph(para) => {
assert_eq!(para.lines.len(), 1);
}
_ => panic!("Expected Paragraph"),
}
}
#[test]
fn test_session_from_title_token() {
let source = "Session:";
let source_location = SourceLocation::new(source);
let title_token = make_line_token(
vec![Token::Text("Session".to_string()), Token::Colon],
vec![0..7, 7..8],
);
let result = session_from_title_token(
&title_token,
Vec::<SessionContent>::new(),
source,
&source_location,
);
match result {
ContentItem::Session(session) => {
assert_eq!(session.title.as_string(), "Session:");
}
_ => panic!("Expected Session"),
}
}
}