From 10cc972f2e4b99e6d3082970ee6982bfee5211c0 Mon Sep 17 00:00:00 2001 From: Andreu Botella Date: Wed, 23 Oct 2019 09:29:24 +0200 Subject: Resolving substitutions. (#9) --- src/parser/simplify.rs | 197 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 141 insertions(+), 56 deletions(-) (limited to 'src/parser/simplify.rs') diff --git a/src/parser/simplify.rs b/src/parser/simplify.rs index 8f699e4..bb47760 100644 --- a/src/parser/simplify.rs +++ b/src/parser/simplify.rs @@ -25,24 +25,55 @@ use crate::target::Target; use crate::document_tree::{ Document, HasChildren, - attribute_types::ID, + attribute_types::{ID, NameToken}, elements as e, element_categories as c, }; -enum MaybeDirectTarget { - IndirectTarget(ID), - DirectTarget(Target), +#[derive(Debug)] +enum NamedTargetType { + NumberedFootnote(usize), + LabeledFootnote(usize), + Citation, + InternalLink, + ExternalLink(Target), + IndirectLink(NameToken), + SectionTitle +} +impl NamedTargetType { + fn is_implicit_target(&self) -> bool { + match self { + NamedTargetType::SectionTitle => true, + _ => false + } + } +} + +#[derive(Clone, Debug)] +struct Substitution { + content: e::ImageInline, + // If `ltrim` is true and the sibling before the reference is a text node, + // the text node gets right-trimmed. Same for `rtrim` with the sibling after + // getting left-trimmed. + ltrim: bool, + rtrim: bool +} + +#[derive(Default, Debug)] +struct TargetsCollected { + named_targets: HashMap, + substitutions: HashMap, + normalized_substitutions: HashMap } trait ResolvableRefs { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>); - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self; + fn populate_targets(&self, refs: &mut TargetsCollected); + fn resolve_refs(self, refs: &TargetsCollected) -> Self; } pub fn resolve_references(mut doc: Document) -> Document { - let mut references = HashMap::new(); + let mut references: TargetsCollected = Default::default(); for c in doc.children() { c.populate_targets(&mut references); } @@ -50,32 +81,32 @@ pub fn resolve_references(mut doc: Document) -> Document { Document::with_children(new) } -fn sub_pop(parent: &P, refs: &mut HashMap<&ID, Target>) where P: HasChildren, C: ResolvableRefs { +fn sub_pop(parent: &P, refs: &mut TargetsCollected) where P: HasChildren, C: ResolvableRefs { for c in parent.children() { c.populate_targets(refs); } } -fn sub_res(mut parent: P, refs: &HashMap<&ID, Target>) -> P where P: e::Element + HasChildren, C: ResolvableRefs { - let new: Vec<_> = parent.children_mut().drain(..).map(|c| c.resolve_refs(&refs)).collect(); +fn sub_res(mut parent: P, refs: &TargetsCollected) -> P where P: e::Element + HasChildren, C: ResolvableRefs { + let new: Vec<_> = parent.children_mut().drain(..).map(|c| c.resolve_refs(refs)).collect(); parent.children_mut().extend(new); parent } -fn sub_sub_pop(parent: &P, refs: &mut HashMap<&ID, Target>) where P: HasChildren, C1: HasChildren, C2: ResolvableRefs { +fn sub_sub_pop(parent: &P, refs: &mut TargetsCollected) where P: HasChildren, C1: HasChildren, C2: ResolvableRefs { for c in parent.children() { sub_pop(c, refs); } } -fn sub_sub_res(mut parent: P, refs: &HashMap<&ID, Target>) -> P where P: e::Element + HasChildren, C1: e::Element + HasChildren, C2: ResolvableRefs { +fn sub_sub_res(mut parent: P, refs: &TargetsCollected) -> P where P: e::Element + HasChildren, C1: e::Element + HasChildren, C2: ResolvableRefs { let new: Vec<_> = parent.children_mut().drain(..).map(|c| sub_res(c, refs)).collect(); parent.children_mut().extend(new); parent } impl ResolvableRefs for c::StructuralSubElement { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::StructuralSubElement::*; match self { Title(e) => sub_pop(&**e, refs), @@ -85,7 +116,7 @@ impl ResolvableRefs for c::StructuralSubElement { SubStructure(e) => e.populate_targets(refs), } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::StructuralSubElement::*; match self { Title(e) => sub_res(*e, refs).into(), @@ -98,7 +129,7 @@ impl ResolvableRefs for c::StructuralSubElement { } impl ResolvableRefs for c::SubStructure { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::SubStructure::*; match self { Topic(e) => sub_pop(&**e, refs), @@ -110,7 +141,7 @@ impl ResolvableRefs for c::SubStructure { BodyElement(e) => e.populate_targets(refs), } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::SubStructure::*; match self { Topic(e) => sub_res(*e, refs).into(), @@ -123,7 +154,8 @@ impl ResolvableRefs for c::SubStructure { } impl ResolvableRefs for c::BodyElement { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { + use crate::document_tree::extra_attributes::ExtraAttributes; use c::BodyElement::*; match self { Paragraph(e) => sub_pop(&**e, refs), @@ -133,8 +165,30 @@ impl ResolvableRefs for c::BodyElement { // TODO }, Rubric(e) => sub_pop(&**e, refs), - SubstitutionDefinition(e) => sub_pop(&**e, refs), - Comment(e) => sub_pop(&**e, refs), + SubstitutionDefinition(e) => { + use e::Element; + let subst_content = if let [c::TextOrInlineElement::ImageInline(content)] = &e.children()[..] { + content.as_ref() + } else { + panic!("Unexpected substitution contents.") + }; + let subst = Substitution { + content: subst_content.clone(), + ltrim: e.extra().ltrim, + rtrim: e.extra().rtrim + }; + for NameToken(name) in e.names() { + if refs.substitutions.contains_key(name) { + // TODO: Duplicate substitution name (level 3 system message). + } + // Intentionally overriding any previous values. + refs.substitutions.insert(name.clone(), subst.clone()); + refs.normalized_substitutions.insert(name.to_lowercase(), subst.clone()); + } + }, + Comment(e) => { + // TODO + }, Pending(e) => { // TODO }, @@ -173,7 +227,7 @@ impl ResolvableRefs for c::BodyElement { Table(e) => sub_pop(&**e, refs) } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::BodyElement::*; match self { Paragraph(e) => sub_res(*e, refs).into(), @@ -181,8 +235,8 @@ impl ResolvableRefs for c::BodyElement { DoctestBlock(e) => sub_res(*e, refs).into(), MathBlock(e) => MathBlock(e), Rubric(e) => sub_res(*e, refs).into(), - SubstitutionDefinition(e) => sub_res(*e, refs).into(), - Comment(e) => sub_res(*e, refs).into(), + SubstitutionDefinition(e) => SubstitutionDefinition(e), + Comment(e) => Comment(e), Pending(e) => Pending(e), Target(e) => Target(e), Raw(e) => Raw(e), @@ -216,7 +270,7 @@ impl ResolvableRefs for c::BodyElement { } impl ResolvableRefs for c::BibliographicElement { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::BibliographicElement::*; match self { Author(e) => sub_pop(&**e, refs), @@ -232,7 +286,7 @@ impl ResolvableRefs for c::BibliographicElement { Field(e) => sub_pop(&**e, refs), } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::BibliographicElement::*; match self { Author(e) => sub_res(*e, refs).into(), @@ -251,7 +305,7 @@ impl ResolvableRefs for c::BibliographicElement { } impl ResolvableRefs for c::TextOrInlineElement { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::TextOrInlineElement::*; match self { c::TextOrInlineElement::String(e) => { @@ -286,7 +340,7 @@ impl ResolvableRefs for c::TextOrInlineElement { } } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::TextOrInlineElement::*; match self { c::TextOrInlineElement::String(e) => c::TextOrInlineElement::String(e), @@ -296,7 +350,38 @@ impl ResolvableRefs for c::TextOrInlineElement { Reference(e) => sub_res(*e, refs).into(), FootnoteReference(e) => sub_res(*e, refs).into(), CitationReference(e) => sub_res(*e, refs).into(), - SubstitutionReference(e) => sub_res(*e, refs).into(), + SubstitutionReference(e) => { + use crate::document_tree::extra_attributes::ExtraAttributes; + if e.extra().refname.len() != 1 { + panic!("Expected exactly one name in a substitution reference."); + } + let name = e.extra().refname[0].0.clone(); + let substitution = refs.substitutions.get(&name).or_else(|| { + refs.normalized_substitutions.get(&name.to_lowercase()) + }); + match substitution { + Some(Substitution {content, ltrim, rtrim}) => { + // TODO: Check if the substitution would expand circularly + // (level 3 system message). + // TODO: Ltrim and rtrim. + ImageInline(Box::new(content.clone())) + }, + None => { + // Undefined substitution name (level 3 system message). + // TODO: This replaces the reference by a Problematic node. + // The corresponding SystemMessage node should go in a generated + // section with class "system-messages" at the end of the document. + use crate::document_tree::{Problematic, element_categories::TextOrInlineElement}; + let mut replacement: Box = Box::new(Default::default()); + replacement.children_mut().push( + TextOrInlineElement::String(Box::new(format!("|{}|", name))) + ); + // TODO: Create an ID for replacement for the system_message to reference. + // TODO: replacement.refid pointing to the system_message. + Problematic(replacement) + } + } + }, TitleReference(e) => sub_res(*e, refs).into(), Abbreviation(e) => sub_res(*e, refs).into(), Acronym(e) => sub_res(*e, refs).into(), @@ -314,7 +399,7 @@ impl ResolvableRefs for c::TextOrInlineElement { } impl ResolvableRefs for c::AuthorInfo { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::AuthorInfo::*; match self { Author(e) => sub_pop(&**e, refs), @@ -323,7 +408,7 @@ impl ResolvableRefs for c::AuthorInfo { Contact(e) => sub_pop(&**e, refs), } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::AuthorInfo::*; match self { Author(e) => sub_res(*e, refs).into(), @@ -335,14 +420,14 @@ impl ResolvableRefs for c::AuthorInfo { } impl ResolvableRefs for c::DecorationElement { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::DecorationElement::*; match self { Header(e) => sub_pop(&**e, refs), Footer(e) => sub_pop(&**e, refs), } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::DecorationElement::*; match self { Header(e) => sub_res(*e, refs).into(), @@ -352,14 +437,14 @@ impl ResolvableRefs for c::DecorationElement { } impl ResolvableRefs for c::SubTopic { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::SubTopic::*; match self { Title(e) => sub_pop(&**e, refs), BodyElement(e) => e.populate_targets(refs), } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::SubTopic::*; match self { Title(e) => sub_res(*e, refs).into(), @@ -369,7 +454,7 @@ impl ResolvableRefs for c::SubTopic { } impl ResolvableRefs for c::SubSidebar { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::SubSidebar::*; match self { Topic(e) => sub_pop(&**e, refs), @@ -378,7 +463,7 @@ impl ResolvableRefs for c::SubSidebar { BodyElement(e) => e.populate_targets(refs), } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::SubSidebar::*; match self { Topic(e) => sub_res(*e, refs).into(), @@ -390,7 +475,7 @@ impl ResolvableRefs for c::SubSidebar { } impl ResolvableRefs for c::SubDLItem { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::SubDLItem::*; match self { Term(e) => sub_pop(&**e, refs), @@ -398,7 +483,7 @@ impl ResolvableRefs for c::SubDLItem { Definition(e) => sub_pop(&**e, refs), } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::SubDLItem::*; match self { Term(e) => sub_res(*e, refs).into(), @@ -409,14 +494,14 @@ impl ResolvableRefs for c::SubDLItem { } impl ResolvableRefs for c::SubField { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::SubField::*; match self { FieldName(e) => sub_pop(&**e, refs), FieldBody(e) => sub_pop(&**e, refs), } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::SubField::*; match self { FieldName(e) => sub_res(*e, refs).into(), @@ -426,14 +511,14 @@ impl ResolvableRefs for c::SubField { } impl ResolvableRefs for c::SubOptionListItem { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::SubOptionListItem::*; match self { OptionGroup(e) => sub_sub_pop(&**e, refs), Description(e) => sub_pop(&**e, refs), } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::SubOptionListItem::*; match self { OptionGroup(e) => sub_sub_res(*e, refs).into(), @@ -443,7 +528,7 @@ impl ResolvableRefs for c::SubOptionListItem { } impl ResolvableRefs for c::SubOption { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::SubOption::*; match self { OptionString(e) => { @@ -454,7 +539,7 @@ impl ResolvableRefs for c::SubOption { }, } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::SubOption::*; match self { OptionString(e) => OptionString(e), @@ -464,14 +549,14 @@ impl ResolvableRefs for c::SubOption { } impl ResolvableRefs for c::SubLineBlock { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::SubLineBlock::*; match self { LineBlock(e) => sub_pop(&**e, refs), Line(e) => sub_pop(&**e, refs), } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::SubLineBlock::*; match self { LineBlock(e) => sub_res(*e, refs).into(), @@ -481,14 +566,14 @@ impl ResolvableRefs for c::SubLineBlock { } impl ResolvableRefs for c::SubBlockQuote { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::SubBlockQuote::*; match self { Attribution(e) => sub_pop(&**e, refs), BodyElement(e) => e.populate_targets(refs), } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::SubBlockQuote::*; match self { Attribution(e) => sub_res(*e, refs).into(), @@ -498,14 +583,14 @@ impl ResolvableRefs for c::SubBlockQuote { } impl ResolvableRefs for c::SubFootnote { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::SubFootnote::*; match self { Label(e) => sub_pop(&**e, refs), BodyElement(e) => e.populate_targets(refs), } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::SubFootnote::*; match self { Label(e) => sub_res(*e, refs).into(), @@ -515,7 +600,7 @@ impl ResolvableRefs for c::SubFootnote { } impl ResolvableRefs for c::SubFigure { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::SubFigure::*; match self { Caption(e) => sub_pop(&**e, refs), @@ -523,7 +608,7 @@ impl ResolvableRefs for c::SubFigure { BodyElement(e) => e.populate_targets(refs), } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::SubFigure::*; match self { Caption(e) => sub_res(*e, refs).into(), @@ -534,14 +619,14 @@ impl ResolvableRefs for c::SubFigure { } impl ResolvableRefs for c::SubTable { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::SubTable::*; match self { Title(e) => sub_pop(&**e, refs), TableGroup(e) => sub_pop(&**e, refs), } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::SubTable::*; match self { Title(e) => sub_res(*e, refs).into(), @@ -551,7 +636,7 @@ impl ResolvableRefs for c::SubTable { } impl ResolvableRefs for c::SubTableGroup { - fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) { + fn populate_targets(&self, refs: &mut TargetsCollected) { use c::SubTableGroup::*; match self { TableColspec(e) => { @@ -569,7 +654,7 @@ impl ResolvableRefs for c::SubTableGroup { }, } } - fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self { + fn resolve_refs(self, refs: &TargetsCollected) -> Self { use c::SubTableGroup::*; match self { TableColspec(e) => TableColspec(e), @@ -585,4 +670,4 @@ impl ResolvableRefs for c::SubTableGroup { }, } } -} \ No newline at end of file +} -- cgit v1.2.3