use std::{
collections::{HashMap, VecDeque},
env,
fmt::Debug,
fs,
path::{Path, PathBuf},
str::FromStr,
};
use log::{error, warn};
use crate::scanner::tokens::{Token, TokenType};
use crate::{
errors::{ParserError, ScannerError},
scanner::Scanner,
};
use crate::{
graph::{
asg::Asg,
blocks::{Block, BlockMacro, Break, LeafBlock, ParentBlock, Section, TableCell},
inlines::{Inline, InlineLiteral, InlineLiteralName, InlineRef, InlineSpan, LineBreak},
lists::{DList, DListItem, List, ListItem, ListVariant},
metadata::{AttributeType, ElementMetadata},
nodes::{Header, Location},
},
utils::{extract_page_ranges, target_and_attrs_from_token},
};
#[derive(Debug)]
pub struct Parser {
origin_directory: PathBuf,
last_token_type: TokenType,
document_header: Option<Header>,
document_attributes: HashMap<String, String>,
block_stack: Vec<Block>,
inline_stack: VecDeque<Inline>,
file_stack: Vec<String>,
block_title: Option<Vec<Inline>>,
metadata: Option<ElementMetadata>,
open_delimited_block_lines: Vec<usize>,
open_parse_after_as_text_type: Option<TokenType>,
level_offset: i8,
in_block_line: bool,
in_inline_span: bool,
in_block_continuation: bool,
force_new_block: bool,
preserve_newline_text: bool,
close_parent_after_push: bool,
dangling_newline: Option<Token>,
pub resolve_targets: bool,
}
impl Default for Parser {
fn default() -> Self {
match env::current_dir() {
Ok(dir) => Self::new(dir),
Err(e) => {
error!("Unexpeced error: {e}");
std::process::exit(1)
}
}
}
}
impl Parser {
pub fn new(origin: PathBuf) -> Self {
let origin_directory = origin
.parent()
.unwrap_or(&env::current_dir().unwrap())
.to_path_buf();
Parser {
origin_directory,
last_token_type: TokenType::Eof,
document_header: None,
document_attributes: HashMap::new(),
block_stack: vec![],
inline_stack: VecDeque::new(),
file_stack: vec![],
block_title: None,
metadata: None,
open_delimited_block_lines: vec![],
level_offset: 0,
in_block_line: false,
in_inline_span: false,
in_block_continuation: false,
preserve_newline_text: false,
open_parse_after_as_text_type: None,
force_new_block: false,
close_parent_after_push: false,
dangling_newline: None,
resolve_targets: true,
}
}
pub fn new_no_target_resolution(origin: PathBuf) -> Self {
let mut test_parser = Self::new(origin);
test_parser.resolve_targets = false;
test_parser
}
pub fn parse<I>(&mut self, tokens: I) -> Result<Asg, ParserError>
where
I: Iterator<Item = Result<Token, ScannerError>>,
{
let mut asg = Asg::new();
for result in tokens {
match result {
Ok(token) => {
let token_type = token.token_type();
self.token_into(token, &mut asg)?;
self.last_token_type = token_type;
}
Err(e) => return Err(ParserError::Scanner(e)),
}
}
self.add_inlines_to_block_stack()?;
while !self.block_stack.is_empty() {
self.add_last_block_to_graph(&mut asg)?;
}
asg.consolidate();
Ok(asg)
}
fn token_into(&mut self, token: Token, asg: &mut Asg) -> Result<(), ParserError> {
if self.document_header.is_some() && !token.can_be_in_document_header() {
self.check_and_move_header(asg)?;
}
if let Some(token_type) = self.open_parse_after_as_text_type {
match token_type {
TokenType::QuoteVerseBlock => {
if token.token_type() == TokenType::QuoteVerseBlock || token.is_inline() {
self.open_parse_after_as_text_type = Some(token_type);
} else {
self.parse_text(token)?;
return Ok(());
}
}
TokenType::PassthroughInlineMacro => {
if [
TokenType::PassthroughInlineMacro,
TokenType::InlineMacroClose,
]
.contains(&token.token_type())
{
self.open_parse_after_as_text_type = Some(token_type)
} else {
self.pass_text_through(token)?;
return Ok(());
}
}
TokenType::PassthroughBlock | TokenType::LiteralBlock | TokenType::CommentBlock => {
if token.token_type() != token_type {
self.pass_text_through(token)?;
return Ok(());
}
}
TokenType::SourceBlock => {
if ![token_type, TokenType::CodeCallout, TokenType::Include]
.contains(&token.token_type())
{
self.pass_text_through(token)?;
return Ok(());
}
}
_ => self.open_parse_after_as_text_type = Some(token_type),
}
}
match token.token_type() {
TokenType::Heading1 => self.parse_title(token, asg),
TokenType::Heading2 => self.parse_section_headings(token, asg),
TokenType::Heading3 => self.parse_section_headings(token, asg),
TokenType::Heading4 => self.parse_section_headings(token, asg),
TokenType::Heading5 => self.parse_section_headings(token, asg),
TokenType::Attribute => self.parse_attribute(token),
TokenType::BlockLabel => {
self.block_title = Some(Vec::new());
self.in_block_line = true;
self.dangling_newline = None;
Ok(())
}
TokenType::BlockAnchor => self.parse_block_anchor_attributes(token),
TokenType::ElementAttributes => self.parse_block_element_attributes(token),
TokenType::InlineStyle => self.parse_inline_element_attributes(token),
TokenType::NewLineChar => self.parse_new_line_char(token, asg),
TokenType::Text => self.parse_text(token),
TokenType::CharRef => self.parse_charref(token),
TokenType::Strong
| TokenType::Mark
| TokenType::Monospace
| TokenType::Literal
| TokenType::Emphasis
| TokenType::Superscript
| TokenType::Subscript
| TokenType::UnconstrainedStrong
| TokenType::UnconstrainedMark
| TokenType::UnconstrainedMonospace
| TokenType::UnconstrainedLiteral
| TokenType::UnconstrainedEmphasis => self.parse_inline_span(Inline::InlineSpan(
InlineSpan::inline_span_from_token(token),
)),
TokenType::OpenDoubleQuote
| TokenType::CloseDoubleQuote
| TokenType::OpenSingleQuote
| TokenType::CloseSingleQuote => self.parse_text(token),
TokenType::AttributeReference => self.parse_attribute_reference(token),
TokenType::CrossReference => self.parse_cross_reference(token),
TokenType::Include => self.parse_include(token, asg),
TokenType::StartTag | TokenType::EndTag => {Ok(())}
TokenType::FootnoteMacro => self.parse_footnote_macro(token),
TokenType::LinkMacro => self.parse_link_macro(token),
TokenType::Hyperlink => self.parse_hyperlink(token),
TokenType::InlineImageMacro => self.parse_inline_image_macro(token),
TokenType::PassthroughInlineMacro => self.parse_passthrough_inline_macro(token),
TokenType::InlineMacroClose => self.parse_inline_macro_close(token),
TokenType::PageBreak => self.parse_page_break(token, asg),
TokenType::ThematicBreak => self.parse_thematic_break(token, asg),
TokenType::LineContinuation => {self
.inline_stack
.push_back(Inline::InlineBreak(LineBreak::new_from_token(token)));Ok(())},
TokenType::SidebarBlock
| TokenType::OpenBlock
| TokenType::ExampleBlock
| TokenType::Table => self.parse_delimited_parent_block(token),
TokenType::TableCell => self.parse_table_cell(token, asg),
TokenType::QuoteVerseBlock => {
if let Some(metadata) = &self.metadata {
if metadata.declared_type == Some(AttributeType::Verse) {
self.parse_delimited_leaf_block(token)?;
return Ok(());
}
} else if self.open_parse_after_as_text_type.is_some() {
self.parse_delimited_leaf_block(token)?;
return Ok(());
}
self.parse_delimited_parent_block(token)?;
Ok(())
}
TokenType::PassthroughBlock | TokenType::LiteralBlock => {
self.parse_delimited_leaf_block(token)
}
TokenType::SourceBlock => self.parse_delimited_leaf_block(token),
TokenType::CodeCallout => self.parse_code_callout(token),
TokenType::BlockImageMacro => self.parse_block_image(token, asg),
TokenType::UnorderedListItem => self.parse_unordered_list_item(token),
TokenType::CodeCalloutListItem | TokenType::OrderedListItem => self.parse_ordered_list_item(token, asg),
TokenType::DescriptionListMarker => self.parse_description_list_term(token),
TokenType::NotePara
| TokenType::TipPara
| TokenType::ImportantPara
| TokenType::CautionPara
| TokenType::WarningPara => self.parse_admonition_para_syntax(token),
TokenType::BlockContinuation => self.parse_block_continuation(token),
TokenType::Comment => self.parse_comment(),
TokenType::CommentBlock => {
if let Some(open_type) = self.open_parse_after_as_text_type {
if open_type == TokenType::CommentBlock {
self.inline_stack.clear();
self.block_stack.pop();
self.force_new_block = true;
self.open_parse_after_as_text_type = None;
}
Ok(())
} else {
self.parse_delimited_leaf_block(token)
}
}
TokenType::Eof => {
if self.document_header.is_some() {
self.check_and_move_header(asg)?
}
Ok(())
}
TokenType::Email => todo!()
}
}
fn parse_attribute(&mut self, token: Token) -> Result<(), ParserError> {
let binding = token.text();
let mut attr_components: Vec<&str> = binding.split_terminator(':').collect();
attr_components.remove(0); if attr_components.is_empty() {
warn!("Empty attributes list at line: {}", token.line);
return Ok(());
}
let key = attr_components.first().unwrap();
let mut value = attr_components.last().unwrap().trim();
if key == &value {
value = ""
}
match *key {
"leveloffset" => self.parse_level_offset(value),
_ => {
self.document_attributes
.insert(key.to_string(), value.to_string());
Ok(())
}
}
}
fn parse_level_offset(&mut self, value: &str) -> Result<(), ParserError> {
if let Ok(value) = value.parse::<usize>() {
self.level_offset = value as i8;
Ok(())
} else {
match value.parse::<i8>() {
Ok(value) => {
self.level_offset += value;
Ok(())
}
Err(_) => Err(ParserError::InternalError(
"Error parsing level offset".to_string(),
)),
}
}
}
fn parse_block_anchor_attributes(&mut self, token: Token) -> Result<(), ParserError> {
self.add_metadata_from_token(token);
self.force_new_block = true;
Ok(())
}
fn parse_block_element_attributes(&mut self, token: Token) -> Result<(), ParserError> {
self.add_metadata_from_token(token);
self.force_new_block = true;
Ok(())
}
fn parse_inline_element_attributes(&mut self, token: Token) -> Result<(), ParserError> {
self.metadata = Some(ElementMetadata::new_inline_meta_from_token(token));
self.force_new_block = true;
Ok(())
}
fn check_and_move_header(&mut self, asg: &mut Asg) -> Result<(), ParserError> {
self.add_inlines_to_block_stack()?;
if let Some(header) = &mut self.document_header {
if !header.is_empty() {
header.consolidate();
asg.add_header(header.clone(), self.document_attributes.clone())
}
}
self.document_header = None;
Ok(())
}
fn parse_new_line_char(&mut self, token: Token, asg: &mut Asg) -> Result<(), ParserError> {
self.in_block_line = false;
if let Some(ref mut title_stack) = self.block_title {
while !self.inline_stack.is_empty() {
let inline = self.inline_stack.pop_front().unwrap();
title_stack.push(inline);
}
}
if [TokenType::NewLineChar, TokenType::Eof].contains(&self.last_token_type) {
self.dangling_newline = None;
if self.document_header.is_some() {
self.check_and_move_header(asg)?;
} else {
if let Some(Block::ListItem(_)) = self.block_stack.last() {
self.add_last_list_item_to_list()?;
} else if let Some(Block::DListItem(_)) = self.block_stack.last() {
self.add_last_list_item_to_list()?;
}
self.in_inline_span = false;
self.add_inlines_to_block_stack()?;
self.force_new_block = true;
if let Some(last_block) = self.block_stack.pop() {
if !last_block.is_section() && self.open_delimited_block_lines.is_empty() {
self.add_to_block_stack_or_graph(asg, last_block)?;
if self.close_parent_after_push && !self.block_stack.is_empty() {
self.add_last_to_block_stack_or_graph(asg)?;
self.close_parent_after_push = false;
}
} else {
self.push_block_to_stack(last_block)?
}
} }
} else if self.in_block_continuation || self.last_token_type.clears_newline_after() {
self.dangling_newline = None;
} else {
self.dangling_newline = Some(token);
}
Ok(())
}
fn parse_thematic_break(&mut self, token: Token, asg: &mut Asg) -> Result<(), ParserError> {
self.add_to_block_stack_or_graph(
asg,
Block::Break(Break::new(
crate::graph::blocks::BreakVariant::Thematic,
token.locations(),
)),
)
}
fn parse_page_break(&mut self, token: Token, asg: &mut Asg) -> Result<(), ParserError> {
self.add_to_block_stack_or_graph(
asg,
Block::Break(Break::new(
crate::graph::blocks::BreakVariant::Page,
token.locations(),
)),
)
}
fn parse_comment(&self) -> Result<(), ParserError> {
Ok(())
}
fn parse_include(&mut self, token: Token, asg: &mut Asg) -> Result<(), ParserError> {
let mut included_lines: Vec<i32> = vec![];
let mut include_to_end: bool = false;
let mut included_tags: Vec<String> = vec![];
let mut asciidoc_include: bool = false;
let mut current_tag: Option<String> = None;
let (target, meta) = target_and_attrs_from_token(&token);
if let Some(metadata) = meta {
if let Some(value) = metadata.attributes.get("leveloffset") {
match self.parse_level_offset(value) {
Ok(_) => {}
Err(e) => return Err(e),
}
}
if let Some(value) = metadata.attributes.get("lines") {
included_lines = extract_page_ranges(value);
if let Some(line) = included_lines.last() {
if *line == -1 {
include_to_end = true;
included_lines.pop();
}
}
}
if let Some(value) = metadata.attributes.get("tag") {
included_tags.push(value.clone());
} if let Some(value) = metadata.attributes.get("tags") {
included_tags.extend(value.split(";").map(|tag| tag.to_string()));
}
}
let resolved_target = self.resolve_target(token.line, &target)?;
self.file_stack.push(target.clone());
let current_block_stack_len = self.block_stack.len();
if matches!(
target.split('.').next_back().unwrap_or(""),
"adoc" | "asciidoc" | "txt" ) {
asciidoc_include = true;
}
for result in Scanner::new_with_stack(&open_file(resolved_target), self.file_stack.clone())
{
match result {
Ok(token) => {
if !included_lines.is_empty() {
if !included_lines.contains(&(token.line as i32)) {
continue;
} else if include_to_end
&& &(token.line as i32) >= included_lines.last().unwrap()
{
included_lines.clear()
}
}
if !included_tags.is_empty() {
if token.token_type() == TokenType::StartTag {
if let Some(tag) = token.tag() {
if included_tags.contains(&tag) {
current_tag = Some(tag);
continue;
}
}
}
if token.token_type() == TokenType::EndTag {
if let Some(tag) = token.tag() {
if current_tag == Some(tag) {
current_tag = None;
continue;
}
}
}
if current_tag.is_none() {
continue;
}
}
if asciidoc_include {
let token_type = token.token_type();
self.token_into(token, asg)?;
self.last_token_type = token_type;
} else {
if matches!(token.token_type(), TokenType::Eof) {
self.last_token_type = TokenType::Eof;
} else {
self.last_token_type = TokenType::Text;
}
self.parse_text(token)?;
}
}
Err(e) => return Err(ParserError::Scanner(e)),
}
}
self.add_inlines_to_block_stack()?;
while self.block_stack.len() > current_block_stack_len {
self.add_last_block_to_graph(asg)?
}
self.file_stack.pop();
Ok(())
}
fn parse_description_list_term(&mut self, token: Token) -> Result<(), ParserError> {
let mut dlist_item = DListItem::new_from_token(token);
self.force_new_block = false;
if let Some(newline_idx) = self
.inline_stack
.iter()
.position(|inline| inline.is_newline())
{
let mut next_terms: VecDeque<Inline> = self.inline_stack.drain(newline_idx..).collect();
next_terms.pop_front();
self.add_inlines_to_block_stack()?;
self.inline_stack.append(&mut next_terms);
}
while !self.inline_stack.is_empty() {
let inline = self.inline_stack.pop_front().unwrap();
dlist_item.push_term(inline);
}
if self.block_stack.last().is_some()
&& self.block_stack.last().unwrap().is_definition_list_item()
{
self.add_last_list_item_to_list()?
} else {
self.push_block_to_stack(Block::DList(DList::new(dlist_item.locations().clone())))?;
}
self.push_block_to_stack(Block::DListItem(dlist_item))?;
self.preserve_newline_text = true;
Ok(())
}
fn parse_ordered_list_item(&mut self, token: Token, asg: &mut Asg) -> Result<(), ParserError> {
self.dangling_newline = None;
let list_item = ListItem::new(token.lexeme.clone(), token.locations());
if self.block_stack.last().is_some()
&& self.block_stack.last().unwrap().is_ordered_list_item()
{
self.add_last_list_item_to_list()?
} else {
let mut list = List::new(ListVariant::Ordered, token.locations().clone());
if token.token_type() == TokenType::CodeCalloutListItem {
if let Some(block) = self.block_stack.last() {
if block.is_source_block() {
self.add_last_to_block_stack_or_graph(asg)?;
}
}
list.metadata = Some(ElementMetadata::new_with_role("colist".to_string()));
}
self.push_block_to_stack(Block::List(list))?;
}
self.push_block_to_stack(Block::ListItem(list_item))
}
fn parse_unordered_list_item(&mut self, token: Token) -> Result<(), ParserError> {
self.dangling_newline = None;
let list_item = ListItem::new(token.lexeme.clone(), token.locations());
if self.block_stack.last().is_some()
&& self.block_stack.last().unwrap().is_unordered_list_item()
{
self.add_last_list_item_to_list()?
} else {
self.push_block_to_stack(Block::List(List::new(
ListVariant::Unordered,
token.locations().clone(),
)))?;
}
self.push_block_to_stack(Block::ListItem(list_item))
}
fn parse_title(&mut self, token: Token, asg: &mut Asg) -> Result<(), ParserError> {
if self.level_offset != 0 {
return self.parse_section_headings(token, asg);
}
if token.first_location() == Location::default() {
self.in_block_line = true;
let mut header = Header::new();
header.location.extend(token.locations());
self.document_header = Some(header);
Ok(())
} else {
Err(ParserError::TopLevelHeading(token.line))
}
}
fn parse_section_headings(&mut self, token: Token, asg: &mut Asg) -> Result<(), ParserError> {
let level = self.get_heading_level(&token)?;
if let Some(Block::Section(_)) = self.block_stack.last() {
self.add_last_to_block_stack_or_graph(asg)?
}
self.push_block_to_stack(Block::Section(Section::new(
"".to_string(),
level,
token.first_location(),
)))?;
self.in_block_line = true;
self.dangling_newline = None;
self.force_new_block = false;
Ok(())
}
fn get_heading_level(&self, token: &Token) -> Result<usize, ParserError> {
let level: i8 = match token.token_type() {
TokenType::Heading1 => self.level_offset,
TokenType::Heading2 => 1 + self.level_offset,
TokenType::Heading3 => 2 + self.level_offset,
TokenType::Heading4 => 3 + self.level_offset,
TokenType::Heading5 => 4 + self.level_offset,
_ => {
return Err(ParserError::InternalError(
"Inavlid token given to parse_section_headings".to_string(),
));
}
};
match level.try_into() {
Ok(value) => {
if !(1..=4).contains(&value) {
Err(ParserError::HeadingOffsetError(
token.line,
self.level_offset,
))
} else {
Ok(value)
}
}
Err(_) => Err(ParserError::HeadingOffsetError(
token.line,
self.level_offset,
)),
}
}
fn parse_admonition_para_syntax(&mut self, token: Token) -> Result<(), ParserError> {
self.block_stack
.push(Block::ParentBlock(ParentBlock::new_from_token(token)?));
self.close_parent_after_push = true;
Ok(())
}
fn parse_block_continuation(&mut self, _token: Token) -> Result<(), ParserError> {
self.add_inlines_to_block_stack()?;
self.in_block_continuation = true;
self.force_new_block = true;
Ok(())
}
fn parse_inline_span(&mut self, mut inline: Inline) -> Result<(), ParserError> {
if let Some(last_inline) = self.inline_stack.back_mut() {
if inline == *last_inline {
last_inline.reconcile_locations(inline.locations());
last_inline.close();
self.in_inline_span = false;
return Ok(());
} else if let Inline::InlineSpan(last_span) = last_inline {
if let Some(last_internal_inline) = last_span.inlines.last_mut() {
if inline == *last_internal_inline {
last_internal_inline.reconcile_locations(inline.locations());
last_internal_inline.close();
} else {
last_span.add_inline(inline);
}
} else {
last_span.add_inline(inline);
}
return Ok(());
}
}
if let Some(newline_token) = self.dangling_newline.clone() {
self.add_text_to_last_inline(newline_token);
self.dangling_newline = None;
}
self.handle_dangling_spans();
if self.metadata.is_some() {
inline.add_metadata(self.metadata.as_ref().unwrap().clone());
self.metadata = None;
}
self.inline_stack.push_back(inline);
self.in_inline_span = true;
Ok(())
}
fn parse_link_macro(&mut self, token: Token) -> Result<(), ParserError> {
self.inline_stack
.push_back(Inline::InlineRef(InlineRef::new_link_from_macro_token(
token,
)));
Ok(())
}
fn parse_hyperlink(&mut self, token: Token) -> Result<(), ParserError> {
self.inline_stack
.push_back(Inline::InlineRef(InlineRef::new_link_from_token(token)));
Ok(())
}
fn parse_block_image(&mut self, token: Token, asg: &mut Asg) -> Result<(), ParserError> {
let (target, metadata) = target_and_attrs_from_token(&token);
self.check_target(token.line, &target)?;
let mut image_block = BlockMacro::new_image_block(target, metadata, token.locations());
if let Some(metadata) = &self.metadata {
image_block = image_block.add_metadata(metadata);
self.metadata = None;
}
if let Some(caption) = &self.block_title {
image_block.caption = caption.clone();
self.block_title = None
}
self.add_to_block_stack_or_graph(asg, Block::BlockMacro(image_block))
}
fn parse_inline_image_macro(&mut self, token: Token) -> Result<(), ParserError> {
let (target, metadata) = target_and_attrs_from_token(&token);
let _ = self.check_target(token.line, &target);
self.inline_stack
.push_back(Inline::InlineRef(InlineRef::new_inline_image(
target,
metadata,
token.locations(),
)));
self.close_parent_after_push = true;
Ok(())
}
fn parse_footnote_macro(&mut self, token: Token) -> Result<(), ParserError> {
self.inline_stack
.push_back(Inline::InlineSpan(InlineSpan::inline_span_from_token(
token,
)));
self.in_inline_span = true;
Ok(())
}
fn parse_passthrough_inline_macro(&mut self, token: Token) -> Result<(), ParserError> {
self.open_parse_after_as_text_type = Some(token.token_type());
Ok(())
}
fn parse_inline_macro_close(&mut self, token: Token) -> Result<(), ParserError> {
if let Some(TokenType::PassthroughInlineMacro) = self.open_parse_after_as_text_type {
self.open_parse_after_as_text_type = None
} else if let Some(inline_macro_idx) = self
.inline_stack
.iter()
.rposition(|inline| inline.is_macro())
{
let mut inline_macro = self.inline_stack.remove(inline_macro_idx).unwrap();
while self.inline_stack.len() > inline_macro_idx {
let subsequent_inline = self.inline_stack.remove(inline_macro_idx).unwrap();
inline_macro.push_inline(subsequent_inline);
}
inline_macro.consolidate_locations_from_token(token);
self.inline_stack.push_back(inline_macro);
self.close_parent_after_push = true;
self.in_inline_span = false;
} else {
self.add_text_to_last_inline(token);
}
Ok(())
}
fn parse_attribute_reference(&mut self, mut token: Token) -> Result<(), ParserError> {
let attribute_ref = token.text();
let attribute_target: &str = &attribute_ref[1..attribute_ref.len() - 1];
if let Some((_, value)) = self.document_attributes.get_key_value(attribute_target) {
token.literal = Some(value.clone());
token.endcol = token.startcol + value.len() - 1;
} else {
warn!("Missing document attribute: {}", attribute_target);
}
self.parse_text(token)?;
Ok(())
}
fn parse_cross_reference(&mut self, token: Token) -> Result<(), ParserError> {
self.inline_stack
.push_back(Inline::InlineRef(InlineRef::new_xref_from_token(token)));
self.close_parent_after_push = true;
Ok(())
}
fn parse_code_callout(&mut self, token: Token) -> Result<(), ParserError> {
if let Some(value) = self.document_attributes.get("icons") {
if value == "true" {
if let Some(_last_inline) = self.inline_stack.back_mut() {
todo!();
}
}
}
self.inline_stack
.push_back(Inline::InlineSpan(InlineSpan::inline_span_from_token(
token,
)));
Ok(())
}
fn parse_text(&mut self, token: Token) -> Result<(), ParserError> {
if let Some(newline_token) = self.dangling_newline.clone() {
if self.preserve_newline_text {
self.inline_stack.push_back(Inline::InlineLiteral(
InlineLiteral::new_text_from_token(&newline_token),
));
self.dangling_newline = None;
self.inline_stack.push_back(Inline::InlineLiteral(
InlineLiteral::new_text_from_token(&token),
));
return Ok(());
} else {
self.add_text_to_last_inline(newline_token);
self.dangling_newline = None;
}
}
self.add_text_to_last_inline(token);
Ok(())
}
fn pass_text_through(&mut self, token: Token) -> Result<(), ParserError> {
if let Some(newline_token) = self.dangling_newline.clone() {
if self.preserve_newline_text {
self.inline_stack.push_back(Inline::InlineLiteral(
InlineLiteral::new_text_from_token(&newline_token),
));
self.dangling_newline = None;
self.inline_stack.push_back(Inline::InlineLiteral(
InlineLiteral::new_text_from_token_pass(&token),
));
return Ok(());
} else {
self.add_text_to_last_inline(newline_token);
self.dangling_newline = None;
}
}
self.pass_text_to_last_inline(token);
Ok(())
}
fn parse_charref(&mut self, token: Token) -> Result<(), ParserError> {
let inline_literal = Inline::InlineLiteral(InlineLiteral::new_charref_from_token(&token));
if let Some(newline_token) = self.dangling_newline.clone() {
if self.preserve_newline_text {
self.inline_stack.push_back(Inline::InlineLiteral(
InlineLiteral::new_text_from_token(&newline_token),
));
self.dangling_newline = None;
self.inline_stack.push_back(inline_literal);
return Ok(());
} else {
self.add_text_to_last_inline(newline_token);
self.dangling_newline = None;
}
}
if let Some(last_inline) = self.inline_stack.back_mut() {
match last_inline {
Inline::InlineSpan(span) => {
if self.in_inline_span {
span.add_inline(inline_literal);
} else {
self.inline_stack.push_back(inline_literal)
}
}
Inline::InlineRef(inline_ref) => {
if !self.close_parent_after_push {
inline_ref.inlines.push(inline_literal);
} else {
self.inline_stack.push_back(inline_literal)
}
}
_ => {
self.inline_stack.push_back(inline_literal)
}
}
} else {
self.inline_stack.push_back(inline_literal)
}
Ok(())
}
fn parse_delimited_leaf_block(&mut self, token: Token) -> Result<(), ParserError> {
if self.open_parse_after_as_text_type.is_some() {
self.add_inlines_to_block_stack()?;
match self.block_stack.pop() {
Some(mut open_leaf) => {
open_leaf.add_locations(token.locations().clone());
self.push_block_to_stack(open_leaf)?;
self.open_parse_after_as_text_type = None;
Ok(())
}
None => Err(ParserError::OpenParse(token.line)),
}
} else {
self.open_parse_after_as_text_type = Some(token.token_type());
let block = LeafBlock::new_from_token(token)?;
self.push_block_to_stack(Block::LeafBlock(block))?;
self.force_new_block = false;
Ok(())
}
}
fn parse_delimited_parent_block(&mut self, token: Token) -> Result<(), ParserError> {
let delimiter_line = token.first_location().line;
let mut block = ParentBlock::new_from_token(token)?;
self.dangling_newline = None;
if self.block_title.is_some() {
block.title = self.block_title.as_ref().unwrap().clone();
self.block_title = None;
}
if let Some(parent_block_idx) = self
.block_stack
.iter()
.rposition(|parent_block| matches!(parent_block, Block::ParentBlock(_)))
{
let matched_block = self.block_stack.remove(parent_block_idx);
let Block::ParentBlock(mut matched) = matched_block else {
return Err(ParserError::ParentBlock(delimiter_line));
};
if matched == block {
self.add_inlines_to_block_stack()?;
let Some(line) = self.open_delimited_block_lines.pop() else {
return Err(ParserError::DelimitedBlock);
};
if line != matched.opening_line()? {
warn!("Error nesting delimited blocks, see line {}", line)
}
matched.location = Location::reconcile(matched.location.clone(), block.location);
let mut blocks_to_add =
VecDeque::from_iter(self.block_stack.drain(parent_block_idx..));
let mut delimited_block = Block::ParentBlock(matched);
while !blocks_to_add.is_empty() {
delimited_block.push_block(blocks_to_add.pop_front().unwrap())?
}
if delimited_block.is_table() {
delimited_block.consolidate_table_info()?;
}
self.push_block_to_stack(delimited_block)?;
self.in_block_continuation = false;
return Ok(());
} else {
self.block_stack
.insert(parent_block_idx, Block::ParentBlock(matched));
}
}
self.open_delimited_block_lines.push(delimiter_line);
self.push_block_to_stack(Block::ParentBlock(block))
}
fn parse_table_cell(&mut self, token: Token, asg: &mut Asg) -> Result<(), ParserError> {
let cell_contents = token.text()[1..].to_string();
let cell_line = token.first_location().line;
let cell_col = token.first_location().col;
self.push_block_to_stack(Block::TableCell(TableCell::new_from_token(token)))?;
self.dangling_newline = None;
for result in Scanner::new(&cell_contents) {
match result {
Ok(mut inline_token) => {
inline_token.update_token_loc_offsets_by(cell_line, cell_col);
self.token_into(inline_token, asg)?;
}
Err(e) => return Err(ParserError::Scanner(e)),
}
}
self.in_block_line = false;
self.force_new_block = false;
self.add_inlines_to_block_stack()?;
Ok(())
}
fn push_block_to_stack(&mut self, mut block: Block) -> Result<(), ParserError> {
if self.in_block_continuation && self.open_delimited_block_lines.is_empty() {
let Some(last_block) = self.block_stack.last_mut() else {
return Err(ParserError::BlockContinuation);
};
last_block.push_block(block)?;
self.in_block_continuation = false;
} else {
if self.metadata.is_some() {
block.add_metadata(self.metadata.as_ref().unwrap().clone())?;
self.metadata = None;
}
self.block_stack.push(block)
}
Ok(())
}
fn add_text_to_last_inline(&mut self, token: Token) {
let inline_literal = Inline::InlineLiteral(InlineLiteral::new_text_from_token(&token));
if let Some(last_inline) = self.inline_stack.back_mut() {
match last_inline {
Inline::InlineSpan(span) => {
if self.in_inline_span {
span.add_inline(inline_literal);
} else {
self.inline_stack.push_back(inline_literal)
}
}
Inline::InlineLiteral(prior_literal) => {
if matches!(prior_literal.name, InlineLiteralName::Charref) {
self.inline_stack.push_back(inline_literal)
} else {
prior_literal.add_text_from_token(&token)
}
}
Inline::InlineRef(inline_ref) => {
if !self.close_parent_after_push {
inline_ref.add_text_from_token(token)
} else {
self.inline_stack.push_back(inline_literal)
}
}
Inline::InlineBreak(_) => {
self.inline_stack.push_back(inline_literal)
}
}
} else {
self.inline_stack.push_back(inline_literal)
}
}
fn pass_text_to_last_inline(&mut self, token: Token) {
let inline_literal = Inline::InlineLiteral(InlineLiteral::new_text_from_token_pass(&token));
if let Some(last_inline) = self.inline_stack.back_mut() {
match last_inline {
Inline::InlineSpan(span) => {
if self.in_inline_span {
span.add_inline(inline_literal);
} else {
self.inline_stack.push_back(inline_literal)
}
}
Inline::InlineLiteral(prior_literal) => {
if matches!(prior_literal.name, InlineLiteralName::Charref) {
self.inline_stack.push_back(inline_literal)
} else {
prior_literal.pass_text_from_token(&token)
}
}
Inline::InlineRef(inline_ref) => {
if !self.close_parent_after_push {
inline_ref.pass_text_from_token(token)
} else {
self.inline_stack.push_back(inline_literal)
}
}
Inline::InlineBreak(_) => {
self.inline_stack.push_back(inline_literal)
}
}
} else {
self.inline_stack.push_back(inline_literal)
}
}
fn handle_dangling_spans(&mut self) {
if let Some(open_span_idx) = self
.inline_stack
.iter()
.rposition(|inline| inline.is_open())
{
let mut open_span = self.inline_stack.remove(open_span_idx).unwrap();
let open_span_literal = open_span.produce_literal_from_self();
let mut children = open_span.extract_child_inlines();
if let Some(inline) = children.front_mut() {
match inline {
Inline::InlineLiteral(literal) => {
literal.prepend_to_value(open_span_literal, open_span.locations());
}
_ => todo!(),
}
while children.len() > 0 {
if let Some(child) = children.pop_back() {
self.inline_stack.insert(open_span_idx, child);
}
}
let mut temp_stack: Vec<Inline> = vec![];
while let Some(mut inline) = self.inline_stack.pop_front() {
if temp_stack.len() == 0 {
temp_stack.push(inline);
} else if inline.is_literal() {
if let Some(Inline::InlineLiteral(last_in_stack)) = temp_stack.last_mut() {
last_in_stack.combine_literals(inline.extract_literal());
} else {
temp_stack.push(inline);
}
} else {
temp_stack.push(inline);
}
}
self.inline_stack = temp_stack.into();
} else {
let (line, startcol, endcol) =
Location::destructure_inline_locations(open_span.locations());
let reconstituted_token = Token {
token_type: TokenType::Text,
lexeme: open_span_literal.clone(),
literal: Some(open_span_literal),
line,
startcol,
endcol,
file_stack: self.file_stack.clone(),
};
self.add_text_to_last_inline(reconstituted_token)
}
}
}
fn add_inlines_to_block_stack(&mut self) -> Result<(), ParserError> {
if self.inline_stack.is_empty() {
return Ok(());
}
if self.in_inline_span {
self.handle_dangling_spans();
}
if let Some(header) = &mut self.document_header {
while !self.inline_stack.is_empty() {
header.title.push(self.inline_stack.pop_front().unwrap());
}
return Ok(());
}
if let Some(last_block) = self.block_stack.last_mut() {
if last_block.takes_inlines() && !self.in_block_line && !self.force_new_block {
while !self.inline_stack.is_empty() {
let inline = self.inline_stack.pop_front().unwrap();
last_block.push_inline(inline)?;
}
return Ok(());
}
}
let mut para_locations: Vec<Location> = Vec::new();
if let Some(first_inline) = self.inline_stack.front() {
para_locations = first_inline.locations().clone();
}
let mut para_block = Block::LeafBlock(LeafBlock::new(
crate::graph::blocks::LeafBlockName::Paragraph,
crate::graph::blocks::LeafBlockForm::Paragraph,
None,
para_locations,
vec![],
));
while !self.inline_stack.is_empty() {
if let Some(inline) = self.inline_stack.pop_front() {
para_block.push_inline(inline)?
}
}
if self.in_block_continuation && self.open_delimited_block_lines.is_empty() {
let Some(last_block) = self.block_stack.last_mut() else {
return Err(ParserError::BlockContinuation);
};
last_block.push_block(para_block)?;
return Ok(());
}
if let Some(ref block_metadata) = self.metadata {
let line_above = para_block.locations().first().unwrap().line + 1;
if self.open_delimited_block_lines.last() == Some(&line_above)
|| self.open_delimited_block_lines.is_empty()
&& block_metadata.declared_type == Some(AttributeType::Quote)
{
let mut quote_block = Block::ParentBlock(ParentBlock::new(
crate::graph::blocks::ParentBlockName::Quote,
None,
"".to_string(),
vec![],
vec![],
));
quote_block.push_block(para_block)?;
self.push_block_to_stack(quote_block)?;
return Ok(());
}
}
self.push_block_to_stack(para_block)
}
fn add_metadata_from_token(&mut self, token: Token) {
match self.metadata {
Some(ref mut metadata) => metadata.add_metadata_from_token(token),
None => {
self.metadata = Some(ElementMetadata::new_block_meta_from_token(token));
}
}
}
fn add_last_list_item_to_list(&mut self) -> Result<(), ParserError> {
self.force_new_block = false;
self.add_inlines_to_block_stack()?;
let last_item = self.block_stack.pop().unwrap();
if matches!(last_item, Block::ListItem(_) | Block::DListItem(_)) {
if let Some(list) = self.block_stack.last_mut() {
list.push_block(last_item)?
}
} else {
self.push_block_to_stack(last_item)?;
}
Ok(())
}
fn add_to_block_stack_or_graph(
&mut self,
asg: &mut Asg,
mut block: Block,
) -> Result<(), ParserError> {
if let Some(last_block) = self.block_stack.last_mut() {
if last_block.takes_block_of_type(&block) {
last_block.push_block(block)?;
return Ok(());
}
}
if self.metadata.is_some() {
block.add_metadata(self.metadata.as_ref().unwrap().clone())?;
self.metadata = None;
}
asg.push_block(block)?;
Ok(())
}
fn add_last_to_block_stack_or_graph(&mut self, asg: &mut Asg) -> Result<(), ParserError> {
if let Some(last_block) = self.block_stack.pop() {
if let Some(prior_block) = self.block_stack.last_mut() {
if prior_block.takes_block_of_type(&last_block) {
prior_block.push_block(last_block)?;
return Ok(());
}
}
asg.push_block(last_block)?;
Ok(())
} else {
Err(ParserError::BlockStack)
}
}
fn add_last_block_to_graph(&mut self, asg: &mut Asg) -> Result<(), ParserError> {
if let Some(mut block) = self.block_stack.pop() {
if self.metadata.is_some() {
block.add_metadata(self.metadata.as_ref().unwrap().clone())?;
self.metadata = None;
}
if let Some(next_last_block) = self.block_stack.last_mut() {
if next_last_block.takes_block_of_type(&block) {
next_last_block.push_block(block)?;
} else {
asg.push_block(block)?
}
} else {
asg.push_block(block)?
}
}
Ok(())
}
fn check_target(&self, token_line: usize, target: &str) -> Result<(), ParserError> {
if self.resolve_targets {
match self.resolve_target(token_line, target) {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
} else {
Ok(())
}
}
fn resolve_target(&self, token_line: usize, target: &str) -> Result<PathBuf, ParserError> {
if !self.file_stack.is_empty() {
let mut resolved_target = self.origin_directory.clone();
for file in self.file_stack.iter() {
if let Some(parent) = PathBuf::from_str(file).unwrap().parent() {
resolved_target.push(parent)
}
}
match resolved_target.join(target).canonicalize() {
Ok(p) => Ok(p),
Err(_) => Err(ParserError::TargetResolution(token_line, target.into())),
}
} else {
match self.origin_directory.join(target).canonicalize() {
Ok(p) => Ok(p),
Err(_) => Err(ParserError::TargetResolution(token_line, target.into())),
}
}
}
}
fn open_file<P>(filename: P) -> String
where
P: AsRef<Path> + Into<PathBuf> + Debug,
{
match fs::read_to_string(&filename) {
Ok(file_string) => file_string,
Err(e) => {
warn!("Unable to read file {:?}: {e}", filename);
std::process::exit(1)
}
}
}