ftml_extraction/
extractor.rs

1#![allow(clippy::result_large_err)]
2
3use crate::errors::FTMLError;
4use crate::open::terms::TermOrList;
5use crate::rules::FTMLElements;
6use flams_ontology::content::declarations::OpenDeclaration;
7use flams_ontology::content::modules::OpenModule;
8use flams_ontology::content::terms::{Arg, ArgMode, Term, Var};
9use flams_ontology::ftml::FTMLKey;
10use flams_ontology::languages::Language;
11use flams_ontology::narration::documents::DocumentStyles;
12use flams_ontology::narration::notations::{NotationComponent, OpNotation};
13use flams_ontology::narration::problems::{
14    AnswerClass, AnswerKind, Choice, CognitiveDimension, FillInSolOption, GradingNote, SolutionData,
15};
16use flams_ontology::narration::sections::SectionLevel;
17use flams_ontology::narration::variables::Variable;
18use flams_ontology::narration::{DocumentElement, LazyDocRef};
19use flams_ontology::uris::{
20    DocumentElementURI, DocumentURI, ModuleURI, Name, NarrativeURI, NarrativeURITrait, SymbolURI,
21    URIRefTrait,
22};
23use flams_ontology::{DocumentRange, Resourcable, Unchecked};
24use flams_utils::id_counters::IdCounter;
25use flams_utils::vecmap::{VecMap, VecSet};
26use std::borrow::Cow;
27use std::str::FromStr;
28
29pub trait FTMLExtractor {
30    type Attr<'a>: Attributes;
31
32    #[cfg(feature = "rdf")]
33    const RDF: bool;
34
35    fn styles(&mut self) -> &mut DocumentStyles;
36
37    #[cfg(feature = "rdf")]
38    fn add_triples<const N: usize>(&mut self, triples: [flams_ontology::rdf::Triple; N]);
39
40    fn get_narrative_uri(&self) -> NarrativeURI;
41    fn get_content_uri(&self) -> Option<&ModuleURI>;
42
43    #[cfg(feature = "rdf")]
44    fn get_document_iri(&self) -> flams_ontology::rdf::NamedNode {
45        use flams_ontology::uris::URIOrRefTrait;
46        self.get_narrative_uri().to_iri()
47    }
48
49    #[cfg(feature = "rdf")]
50    fn get_content_iri(&self) -> Option<flams_ontology::rdf::NamedNode> {
51        use flams_ontology::uris::URIOrRefTrait;
52        self.get_content_uri().map(URIOrRefTrait::to_iri)
53    }
54
55    fn with_problem<R>(&mut self, then: impl FnOnce(&mut ProblemState) -> R) -> Option<R>;
56
57    fn resolve_variable_name(&self, name: Name) -> Var;
58    fn add_error(&mut self, err: FTMLError);
59    fn add_module(&mut self, module: OpenModule<Unchecked>);
60    fn new_id(&mut self, prefix: Cow<'static, str>) -> Box<str>;
61    fn in_notation(&self) -> bool;
62    fn in_term(&self) -> bool;
63    fn set_in_term(&mut self, b: bool);
64    fn add_document_element(&mut self, elem: DocumentElement<Unchecked>);
65    /// ### Errors
66    fn add_content_element(
67        &mut self,
68        elem: OpenDeclaration<Unchecked>,
69    ) -> Result<(), OpenDeclaration<Unchecked>>;
70
71    fn open_content(&mut self, uri: ModuleURI);
72    fn open_narrative(&mut self, uri: Option<NarrativeURI>);
73    fn open_complex_term(&mut self);
74    fn close_content(&mut self) -> Option<(ModuleURI, Vec<OpenDeclaration<Unchecked>>)>;
75    fn close_narrative(&mut self) -> Option<(NarrativeURI, Vec<DocumentElement<Unchecked>>)>;
76    fn close_complex_term(&mut self) -> Option<Term>;
77    fn open_section(&mut self, uri: DocumentElementURI);
78    fn close_section(
79        &mut self,
80    ) -> Option<(
81        DocumentElementURI,
82        Option<DocumentRange>,
83        Vec<DocumentElement<Unchecked>>,
84    )>;
85    fn open_slide(&mut self);
86    fn close_slide(&mut self) -> Option<Vec<DocumentElement<Unchecked>>>;
87    fn open_paragraph(&mut self, uri: DocumentElementURI, fors: VecSet<SymbolURI>);
88    fn close_paragraph(&mut self) -> Option<ParagraphState>;
89    fn open_problem(&mut self, uri: DocumentElementURI);
90    fn close_problem(&mut self) -> Option<ProblemState>;
91    fn open_gnote(&mut self);
92    fn close_gnote(&mut self) -> Option<GnoteState>;
93    fn open_choice_block(&mut self, multiple: bool, styles: Box<[Box<str>]>);
94    fn close_choice_block(&mut self) -> Option<ChoiceBlockState>;
95    fn open_fillinsol(&mut self, width: Option<f32>);
96    fn close_fillinsol(&mut self) -> Option<FillinsolState>;
97    fn push_fillinsol_case(&mut self, case: FillInSolOption);
98    fn push_answer_class(&mut self, id: Box<str>, kind: AnswerKind);
99    fn push_problem_choice(&mut self, correct: bool);
100
101    fn set_document_title(&mut self, title: Box<str>);
102    /// #### Errors
103    fn add_title(&mut self, title: DocumentRange) -> Result<(), DocumentRange>;
104    fn open_decl(&mut self);
105    fn close_decl(&mut self) -> Option<(Option<Term>, Option<Term>)>;
106    fn open_notation(&mut self);
107    fn close_notation(&mut self) -> Option<NotationState>;
108    fn open_args(&mut self);
109    fn close_args(&mut self) -> (Vec<Arg>, Option<Term>);
110
111    fn add_precondition(&mut self, uri: SymbolURI, dim: CognitiveDimension);
112    fn add_objective(&mut self, uri: SymbolURI, dim: CognitiveDimension);
113    /// #### Errors
114    #[allow(clippy::result_unit_err)]
115    fn add_arg(&mut self, pos: (u8, Option<u8>), tm: Term, mode: ArgMode) -> Result<(), ()>;
116
117    fn add_definiendum(&mut self, uri: SymbolURI);
118
119    fn add_resource<T: Resourcable>(&mut self, t: &T) -> LazyDocRef<T>;
120    /// #### Errors
121    fn add_notation(&mut self, spec: NotationSpec) -> Result<(), NotationSpec>;
122    /// #### Errors
123    fn add_op_notation(&mut self, op: OpNotation) -> Result<(), OpNotation>;
124    /// #### Errors
125    fn add_type(&mut self, tm: Term) -> Result<(), Term>;
126    /// #### Errors
127    fn add_term(&mut self, symbol: Option<SymbolURI>, tm: Term) -> Result<(), Term>;
128}
129
130pub trait Attributes {
131    type KeyIter<'a>: Iterator<Item = &'a str>
132    where
133        Self: 'a;
134    type Value<'a>: AsRef<str> + Into<Cow<'a, str>> + Into<String>
135    where
136        Self: 'a;
137    fn keys(&self) -> Self::KeyIter<'_>;
138    fn value(&self, key: &str) -> Option<Self::Value<'_>>;
139    fn set(&mut self, key: &str, value: &str);
140    fn take(&mut self, key: &str) -> Option<String>;
141
142    #[inline]
143    fn get(&self, tag: FTMLKey) -> Option<Self::Value<'_>> {
144        self.value(tag.attr_name())
145    }
146    #[inline]
147    fn remove(&mut self, tag: FTMLKey) -> Option<String> {
148        self.take(tag.attr_name())
149    }
150
151    /// #### Errors
152    fn get_typed<E, T>(
153        &self,
154        key: FTMLKey,
155        f: impl FnOnce(&str) -> Result<T, E>,
156    ) -> Result<T, FTMLError> {
157        let Some(v) = self.get(key) else {
158            return Err(FTMLError::InvalidKeyFor(key.as_str(), None));
159        };
160        f(v.as_ref()).map_err(|_| FTMLError::InvalidKeyFor(key.as_str(), Some(v.into())))
161    }
162
163    /// #### Errors
164    fn get_typed_vec<E, T>(
165        &self,
166        key: FTMLKey,
167        mut f: impl FnMut(&str) -> Result<T, E>,
168    ) -> Result<Vec<T>, FTMLError> {
169        let Some(v) = self.get(key) else {
170            return Err(FTMLError::InvalidKeyFor(key.as_str(), None));
171        };
172        let mut vec = Vec::new();
173        for e in v.as_ref().split(',') {
174            match f(e) {
175                Ok(v) => vec.push(v),
176                Err(_) => return Err(FTMLError::InvalidKeyFor(key.as_str(), Some(v.into()))),
177            }
178        }
179        Ok(vec)
180    }
181
182    /// #### Errors
183    fn take_typed<E, T>(
184        &mut self,
185        key: FTMLKey,
186        f: impl FnOnce(&str) -> Result<T, E>,
187    ) -> Result<T, FTMLError> {
188        let Some(v) = self.remove(key) else {
189            return Err(FTMLError::InvalidKeyFor(key.as_str(), None));
190        };
191        f(v.as_ref()).map_err(|_| FTMLError::InvalidKeyFor(key.as_str(), Some(v)))
192    }
193
194    /// #### Errors
195    fn get_section_level(&self, key: FTMLKey) -> Result<SectionLevel, FTMLError> {
196        use std::str::FromStr;
197        let Some(v) = self.get(key) else {
198            return Err(FTMLError::InvalidKeyFor(key.as_str(), None));
199        };
200        let Ok(u) = u8::from_str(v.as_ref()) else {
201            return Err(FTMLError::InvalidKeyFor(key.as_str(), Some(v.into())));
202        };
203        SectionLevel::try_from(u)
204            .map_err(|()| FTMLError::InvalidKeyFor(key.as_str(), Some(v.into())))
205    }
206
207    /// #### Errors
208    fn take_section_level(&mut self, key: FTMLKey) -> Result<SectionLevel, FTMLError> {
209        use std::str::FromStr;
210        let Some(v) = self.remove(key) else {
211            return Err(FTMLError::InvalidKeyFor(key.as_str(), None));
212        };
213        let Ok(u) = u8::from_str(v.as_ref()) else {
214            return Err(FTMLError::InvalidKeyFor(key.as_str(), Some(v)));
215        };
216        SectionLevel::try_from(u).map_err(|()| FTMLError::InvalidKeyFor(key.as_str(), Some(v)))
217    }
218
219    /// #### Errors
220    #[inline]
221    fn get_language(&self, key: FTMLKey) -> Result<Language, FTMLError> {
222        self.get_typed(key, Language::from_str)
223    }
224
225    /// #### Errors
226    #[inline]
227    fn take_language(&mut self, key: FTMLKey) -> Result<Language, FTMLError> {
228        self.take_typed(key, Language::from_str)
229    }
230
231    /// #### Errors
232    #[inline]
233    fn get_module_uri<E: FTMLExtractor>(
234        &self,
235        key: FTMLKey,
236        _extractor: &mut E,
237    ) -> Result<ModuleURI, FTMLError> {
238        self.get_typed(key, ModuleURI::from_str)
239    }
240
241    /// #### Errors
242    fn get_new_module_uri<E: FTMLExtractor>(
243        &self,
244        key: FTMLKey,
245        extractor: &mut E,
246    ) -> Result<ModuleURI, FTMLError> {
247        let Some(v) = self.get(key) else {
248            return Err(FTMLError::InvalidKeyFor(key.as_str(), None));
249        };
250        extractor
251            .get_content_uri()
252            .map_or_else(
253                || {
254                    extractor
255                        .get_narrative_uri()
256                        .document()
257                        .module_uri_from(v.as_ref())
258                },
259                |m| m.clone() / v.as_ref(),
260            )
261            .map_err(|_| FTMLError::InvalidURI(format!("1: {}", v.as_ref())))
262    }
263
264    /// #### Errors
265    #[inline]
266    fn take_module_uri<E: FTMLExtractor>(
267        &mut self,
268        key: FTMLKey,
269        _extractor: &mut E,
270    ) -> Result<ModuleURI, FTMLError> {
271        self.take_typed(key, ModuleURI::from_str)
272    }
273
274    /// #### Errors
275    fn take_new_module_uri<E: FTMLExtractor>(
276        &mut self,
277        key: FTMLKey,
278        extractor: &mut E,
279    ) -> Result<ModuleURI, FTMLError> {
280        let Some(v) = self.remove(key) else {
281            return Err(FTMLError::InvalidKeyFor(key.as_str(), None));
282        };
283        extractor
284            .get_content_uri()
285            .map_or_else(
286                || extractor.get_narrative_uri().document().module_uri_from(&v),
287                |m| m.clone() / v.as_str(),
288            )
289            .map_err(|_| FTMLError::InvalidURI(format!("2: {v}")))
290    }
291
292    /// #### Errors
293    #[inline]
294    fn get_symbol_uri<E: FTMLExtractor>(
295        &self,
296        key: FTMLKey,
297        _extractor: &mut E,
298    ) -> Result<SymbolURI, FTMLError> {
299        self.get_typed(key, SymbolURI::from_str)
300    }
301
302    /// #### Errors
303    fn get_new_symbol_uri<E: FTMLExtractor>(
304        &self,
305        key: FTMLKey,
306        extractor: &mut E,
307    ) -> Result<SymbolURI, FTMLError> {
308        let Some(v) = self.get(key) else {
309            return Err(FTMLError::InvalidKeyFor(key.as_str(), None));
310        };
311        let Some(module) = extractor.get_content_uri() else {
312            return Err(FTMLError::NotInContent);
313        };
314        (module.owned() | v.as_ref())
315            .map_err(|_| FTMLError::InvalidURI(format!("3: {}", v.as_ref())))
316    }
317
318    /// #### Errors
319    #[inline]
320    fn take_symbol_uri<E: FTMLExtractor>(
321        &mut self,
322        key: FTMLKey,
323        _extractor: &mut E,
324    ) -> Result<SymbolURI, FTMLError> {
325        self.take_typed(key, SymbolURI::from_str)
326    }
327
328    /// #### Errors
329    fn take_new_symbol_uri<E: FTMLExtractor>(
330        &mut self,
331        key: FTMLKey,
332        extractor: &mut E,
333    ) -> Result<SymbolURI, FTMLError> {
334        let Some(v) = self.remove(key) else {
335            return Err(FTMLError::InvalidKeyFor(key.as_str(), None));
336        };
337        let Some(module) = extractor.get_content_uri() else {
338            return Err(FTMLError::NotInContent);
339        };
340        (module.owned() | v.as_str()).map_err(|_| FTMLError::InvalidURI(format!("4: {v}")))
341    }
342
343    /// #### Errors
344    #[inline]
345    fn get_document_uri<E: FTMLExtractor>(
346        &self,
347        key: FTMLKey,
348        _extractor: &mut E,
349    ) -> Result<DocumentURI, FTMLError> {
350        self.get_typed(key, DocumentURI::from_str)
351    }
352
353    /// #### Errors
354    #[inline]
355    fn take_document_uri<E: FTMLExtractor>(
356        &mut self,
357        key: FTMLKey,
358        _extractor: &mut E,
359    ) -> Result<DocumentURI, FTMLError> {
360        self.take_typed(key, DocumentURI::from_str)
361    }
362
363    fn get_id<E: FTMLExtractor>(&self, extractor: &mut E, prefix: Cow<'static, str>) -> Box<str> {
364        self.get(FTMLKey::Id).map_or_else(
365            || extractor.new_id(prefix),
366            |v| {
367                let v = v.as_ref();
368                if v.starts_with("http") && v.contains('?') {
369                    v.rsplit_once('?')
370                        .unwrap_or_else(|| unreachable!())
371                        .1
372                        .into()
373                } else {
374                    Into::<String>::into(v).into_boxed_str()
375                }
376            },
377        )
378    }
379
380    fn get_bool(&self, key: FTMLKey) -> bool {
381        self.get(key)
382            .and_then(|s| s.as_ref().parse().ok())
383            .unwrap_or_default()
384    }
385
386    fn take_bool(&mut self, key: FTMLKey) -> bool {
387        self.remove(key)
388            .and_then(|s| s.parse().ok())
389            .unwrap_or_default()
390    }
391}
392
393pub trait FTMLNode {
394    type Ancestors<'a>: Iterator<Item = Self>
395    where
396        Self: 'a;
397    fn ancestors(&self) -> Self::Ancestors<'_>;
398    fn with_elements<R>(&mut self, f: impl FnMut(Option<&mut FTMLElements>) -> R) -> R;
399    fn delete(&self);
400    fn delete_children(&self);
401    fn range(&self) -> DocumentRange;
402    fn inner_range(&self) -> DocumentRange;
403    fn string(&self) -> String;
404    fn inner_string(&self) -> String;
405    fn as_notation(&self) -> Option<NotationSpec>;
406    fn as_op_notation(&self) -> Option<OpNotation>;
407    fn as_term(&self) -> Term;
408}
409
410#[derive(Debug)]
411pub struct ParagraphState {
412    pub uri: DocumentElementURI,
413    pub children: Vec<DocumentElement<Unchecked>>,
414    pub fors: VecMap<SymbolURI, Option<Term>>,
415    pub title: Option<DocumentRange>,
416}
417
418#[derive(Clone, Debug)]
419pub struct NotationState {
420    pub attribute_index: u8,
421    pub inner_index: u16,
422    pub is_text: bool,
423    pub components: Box<[NotationComponent]>,
424    pub op: Option<OpNotation>,
425}
426
427#[derive(Debug)]
428pub struct ProblemState {
429    pub uri: DocumentElementURI,
430    pub solutions: Vec<SolutionData>,
431    pub gnote: Option<GnoteState>,
432    pub choice_block: Option<ChoiceBlockState>,
433    pub fillinsol: Option<FillinsolState>,
434    pub hints: Vec<DocumentRange>,
435    pub notes: Vec<LazyDocRef<Box<str>>>,
436    pub gnotes: Vec<LazyDocRef<GradingNote>>,
437    pub title: Option<DocumentRange>,
438    pub children: Vec<DocumentElement<Unchecked>>,
439    pub preconditions: Vec<(CognitiveDimension, SymbolURI)>,
440    pub objectives: Vec<(CognitiveDimension, SymbolURI)>,
441}
442impl ProblemState {
443    #[must_use]
444    pub const fn new(uri: DocumentElementURI) -> Self {
445        Self {
446            uri,
447            solutions: Vec::new(),
448            gnote: None,
449            choice_block: None,
450            hints: Vec::new(),
451            fillinsol: None,
452            gnotes: Vec::new(),
453            notes: Vec::new(),
454            title: None,
455            children: Vec::new(),
456            preconditions: Vec::new(),
457            objectives: Vec::new(),
458        }
459    }
460}
461
462#[derive(Debug)]
463pub struct GnoteState {
464    pub answer_classes: Vec<AnswerClass>,
465}
466
467#[derive(Debug)]
468pub struct ChoiceBlockState {
469    pub multiple: bool,
470    pub inline: bool,
471    pub styles: Box<[Box<str>]>,
472    pub choices: Vec<Choice>,
473}
474
475#[derive(Debug)]
476pub struct FillinsolState {
477    pub cases: Vec<FillInSolOption>,
478}
479
480pub struct NotationSpec {
481    pub attribute_index: u8,
482    pub inner_index: u16,
483    pub is_text: bool,
484    pub components: Box<[NotationComponent]>,
485}
486
487#[cfg(feature = "full")]
488#[derive(Debug)]
489#[allow(clippy::large_enum_variant)]
490pub enum Narrative {
491    Container(NarrativeURI, Vec<DocumentElement<Unchecked>>),
492    Paragraph(ParagraphState),
493    Section {
494        uri: DocumentElementURI,
495        title: Option<DocumentRange>,
496        children: Vec<DocumentElement<Unchecked>>,
497    },
498    Slide {
499        children: Vec<DocumentElement<Unchecked>>,
500    },
501    Problem(ProblemState),
502    Notation(NotationState),
503}
504
505#[cfg(feature = "full")]
506#[derive(Debug)]
507#[allow(clippy::large_enum_variant)]
508pub enum Content {
509    Container(ModuleURI, Vec<OpenDeclaration<Unchecked>>),
510    SingleTerm(Option<Term>),
511    Symdecl { tp: Option<Term>, df: Option<Term> },
512    Args(Vec<Option<(TermOrList, ArgMode)>>, Option<Term>),
513}
514
515#[cfg(feature = "full")]
516#[derive(Debug)]
517pub struct ExtractorState {
518    pub(crate) in_term: bool,
519    pub(crate) ids: IdCounter,
520    pub(crate) narrative: Vec<Narrative>,
521    pub(crate) content: Vec<Content>,
522    pub(crate) modules: Vec<OpenModule<Unchecked>>,
523    pub(crate) styles: DocumentStyles,
524}
525#[cfg(feature = "full")]
526impl ExtractorState {
527    #[must_use]
528    pub fn document_uri(&self) -> &DocumentURI {
529        let Some(Narrative::Container(NarrativeURI::Document(ref ret), _)) =
530            self.narrative.first().as_ref()
531        else {
532            unreachable!()
533        };
534        ret
535    }
536    #[must_use]
537    pub fn new(document: DocumentURI) -> Self {
538        Self {
539            in_term: false,
540            ids: IdCounter::default(),
541            narrative: vec![Narrative::Container(document.into(), Vec::new())],
542            content: Vec::new(),
543            styles: DocumentStyles::default(),
544            modules: Vec::new(),
545        }
546    }
547    /// #### Errors
548    #[allow(clippy::result_unit_err)]
549    #[allow(clippy::type_complexity)]
550    pub fn take(
551        mut self,
552    ) -> Result<
553        (
554            DocumentURI,
555            Vec<DocumentElement<Unchecked>>,
556            Vec<OpenModule<Unchecked>>,
557            DocumentStyles,
558        ),
559        (),
560    > {
561        if self.narrative.len() == 1 {
562            let Some(Narrative::Container(document, elements)) = self.narrative.pop() else {
563                unreachable!()
564            };
565            match document {
566                NarrativeURI::Document(d) => Ok((d, elements, self.modules, self.styles)),
567                NarrativeURI::Element(_) => Err(()),
568            }
569        } else {
570            Err(())
571        }
572    }
573    pub(crate) fn push_narr(&mut self, uri: Option<NarrativeURI>) {
574        let uri = uri.unwrap_or_else(|| {
575            self.narrative
576                .iter()
577                .rev()
578                .find_map(|t| match t {
579                    Narrative::Container(uri, _) => Some(uri.clone()),
580                    _ => None,
581                })
582                .unwrap_or_else(|| unreachable!())
583        });
584        self.narrative.push(Narrative::Container(uri, Vec::new()));
585    }
586}
587
588#[cfg(feature = "full")]
589pub trait StatefulExtractor {
590    type Attr<'a>: Attributes;
591    #[cfg(feature = "rdf")]
592    const RDF: bool;
593    #[cfg(feature = "rdf")]
594    fn add_triples<const N: usize>(&mut self, triples: [flams_ontology::rdf::Triple; N]);
595
596    fn state_mut(&mut self) -> &mut ExtractorState;
597    fn state(&self) -> &ExtractorState;
598    fn add_error(&mut self, err: FTMLError);
599    fn set_document_title(&mut self, title: Box<str>);
600    fn add_resource<T: Resourcable>(&mut self, t: &T) -> LazyDocRef<T>;
601}
602#[cfg(feature = "full")]
603impl<E: StatefulExtractor> FTMLExtractor for E {
604    type Attr<'a> = <Self as StatefulExtractor>::Attr<'a>;
605    #[cfg(feature = "rdf")]
606    const RDF: bool = <Self as StatefulExtractor>::RDF;
607    #[cfg(feature = "rdf")]
608    fn add_triples<const N: usize>(&mut self, triples: [flams_ontology::rdf::Triple; N]) {
609        <Self as StatefulExtractor>::add_triples(self, triples);
610    }
611    fn add_error(&mut self, err: FTMLError) {
612        <Self as StatefulExtractor>::add_error(self, err);
613    }
614
615    fn styles(&mut self) -> &mut DocumentStyles {
616        &mut self.state_mut().styles
617    }
618
619    #[inline]
620    fn set_document_title(&mut self, title: Box<str>) {
621        <Self as StatefulExtractor>::set_document_title(self, title);
622    }
623
624    #[inline]
625    fn add_resource<T: Resourcable>(&mut self, t: &T) -> LazyDocRef<T> {
626        <Self as StatefulExtractor>::add_resource(self, t)
627    }
628
629    fn resolve_variable_name(&self, name: Name) -> Var {
630        let names = name.steps();
631        for n in self.state().narrative.iter().rev() {
632            let ch = match n {
633                Narrative::Container(_, c) => c,
634                Narrative::Problem(ProblemState { children, .. })
635                | Narrative::Section { children, .. }
636                | Narrative::Paragraph(ParagraphState { children, .. })
637                | Narrative::Slide { children, .. } => children,
638                Narrative::Notation(_) => continue,
639            };
640            for c in ch.iter().rev() {
641                match c {
642                    DocumentElement::Variable(Variable { uri, is_seq, .. })
643                        if uri.name().steps().ends_with(names) =>
644                    {
645                        return Var::Ref {
646                            declaration: uri.clone(),
647                            is_sequence: Some(*is_seq),
648                        }
649                    }
650                    _ => (),
651                }
652            }
653        }
654        Var::Name(name)
655    }
656
657    fn open_content(&mut self, uri: ModuleURI) {
658        self.state_mut()
659            .content
660            .push(Content::Container(uri, Vec::new()));
661    }
662    fn open_narrative(&mut self, uri: Option<NarrativeURI>) {
663        self.state_mut().push_narr(uri);
664    }
665    fn open_complex_term(&mut self) {
666        self.state_mut().content.push(Content::SingleTerm(None));
667    }
668    fn close_content(&mut self) -> Option<(ModuleURI, Vec<OpenDeclaration<Unchecked>>)> {
669        match self.state_mut().content.pop() {
670            Some(Content::Container(uri, elements)) => return Some((uri, elements)),
671            Some(o) => self.state_mut().content.push(o),
672            None => {}
673        }
674        None
675    }
676    fn close_narrative(&mut self) -> Option<(NarrativeURI, Vec<DocumentElement<Unchecked>>)> {
677        let state = self.state_mut();
678        let r = state.narrative.pop().unwrap_or_else(|| unreachable!());
679        if state.narrative.is_empty() {
680            state.narrative.push(r);
681            return None;
682        }
683        if let Narrative::Container(uri, elements) = r {
684            Some((uri, elements))
685        } else {
686            state.narrative.push(r);
687            None
688        }
689    }
690
691    fn with_problem<R>(&mut self, then: impl FnOnce(&mut ProblemState) -> R) -> Option<R> {
692        let state = self.state_mut();
693        for e in state.narrative.iter_mut().rev() {
694            if let Narrative::Problem(e) = e {
695                return Some(then(e));
696            }
697        }
698        None
699    }
700
701    fn push_answer_class(&mut self, id: Box<str>, kind: AnswerKind) {
702        if !self
703            .with_problem(|e| {
704                if let Some(gnote) = e.gnote.as_mut() {
705                    gnote.answer_classes.push(AnswerClass {
706                        id,
707                        kind,
708                        feedback: "".into(),
709                    });
710                    true
711                } else {
712                    false
713                }
714            })
715            .unwrap_or_default()
716        {
717            self.add_error(FTMLError::NotInProblem("1"));
718        }
719    }
720
721    fn push_problem_choice(&mut self, correct: bool) {
722        if !self
723            .with_problem(|ex| {
724                if let Some(block) = &mut ex.choice_block {
725                    block.choices.push(Choice {
726                        correct,
727                        verdict: Box::default(),
728                        feedback: Box::default(),
729                    });
730                    true
731                } else {
732                    false
733                }
734            })
735            .unwrap_or_default()
736        {
737            self.add_error(FTMLError::NotInProblem("2"));
738        }
739    }
740
741    fn push_fillinsol_case(&mut self, case: FillInSolOption) {
742        if !self
743            .with_problem(|ex| {
744                if let Some(fillin) = &mut ex.fillinsol {
745                    fillin.cases.push(case);
746                    true
747                } else {
748                    false
749                }
750            })
751            .unwrap_or_default()
752        {
753            self.add_error(FTMLError::NotInProblem("3"));
754        }
755    }
756
757    fn close_complex_term(&mut self) -> Option<Term> {
758        match self.state_mut().content.pop() {
759            Some(Content::SingleTerm(t)) => return t,
760            Some(o) => self.state_mut().content.push(o),
761            None => {}
762        }
763        None
764    }
765
766    fn open_section(&mut self, uri: DocumentElementURI) {
767        self.state_mut().narrative.push(Narrative::Section {
768            title: None,
769            children: Vec::new(),
770            uri,
771        });
772    }
773    fn close_section(
774        &mut self,
775    ) -> Option<(
776        DocumentElementURI,
777        Option<DocumentRange>,
778        Vec<DocumentElement<Unchecked>>,
779    )> {
780        match self.state_mut().narrative.pop() {
781            Some(Narrative::Section {
782                title,
783                children,
784                uri,
785            }) => return Some((uri, title, children)),
786            Some(o) => self.state_mut().narrative.push(o),
787            None => {}
788        }
789        None
790    }
791
792    fn open_slide(&mut self) {
793        self.state_mut().narrative.push(Narrative::Slide {
794            children: Vec::new(),
795        });
796    }
797    fn close_slide(&mut self) -> Option<Vec<DocumentElement<Unchecked>>> {
798        match self.state_mut().narrative.pop() {
799            Some(Narrative::Slide { children }) => return Some(children),
800            Some(o) => self.state_mut().narrative.push(o),
801            None => {}
802        }
803        None
804    }
805
806    fn open_paragraph(&mut self, uri: DocumentElementURI, fors: VecSet<SymbolURI>) {
807        let fors = fors.into_iter().map(|s| (s, None)).collect();
808        self.state_mut()
809            .narrative
810            .push(Narrative::Paragraph(ParagraphState {
811                uri,
812                children: Vec::new(),
813                fors,
814                title: None,
815            }));
816    }
817    fn close_paragraph(&mut self) -> Option<ParagraphState> {
818        match self.state_mut().narrative.pop() {
819            Some(Narrative::Paragraph(state)) => return Some(state),
820            Some(o) => self.state_mut().narrative.push(o),
821            None => {}
822        }
823        None
824    }
825    fn open_gnote(&mut self) {
826        if !self
827            .with_problem(|e| {
828                if e.gnote.is_some() {
829                    false
830                } else {
831                    e.gnote = Some(GnoteState {
832                        answer_classes: Vec::new(),
833                    });
834                    true
835                }
836            })
837            .unwrap_or_default()
838        {
839            self.add_error(FTMLError::NotInProblem("4"));
840        }
841    }
842
843    fn close_gnote(&mut self) -> Option<GnoteState> {
844        self.with_problem(|e| e.gnote.take()).flatten()
845    }
846
847    fn open_fillinsol(&mut self, _width: Option<f32>) {
848        if !self
849            .with_problem(|ex| {
850                if ex.fillinsol.is_some() {
851                    false
852                } else {
853                    ex.fillinsol = Some(FillinsolState { cases: Vec::new() });
854                    true
855                }
856            })
857            .unwrap_or_default()
858        {
859            self.add_error(FTMLError::NotInProblem("5"));
860        }
861    }
862
863    fn close_fillinsol(&mut self) -> Option<FillinsolState> {
864        self.with_problem(|e| e.fillinsol.take()).flatten()
865    }
866
867    fn open_choice_block(&mut self, multiple: bool, styles: Box<[Box<str>]>) {
868        if !self
869            .with_problem(|e| {
870                if e.choice_block.is_some() {
871                    false
872                } else {
873                    e.choice_block = Some(ChoiceBlockState {
874                        multiple,
875                        inline: styles.iter().any(|s| &**s == "inline"),
876                        styles,
877                        choices: Vec::new(),
878                    });
879                    true
880                }
881            })
882            .unwrap_or_default()
883        {
884            self.add_error(FTMLError::NotInProblem("6"));
885        }
886    }
887    fn close_choice_block(&mut self) -> Option<ChoiceBlockState> {
888        self.with_problem(|e| e.choice_block.take()).flatten()
889    }
890
891    fn open_problem(&mut self, uri: DocumentElementURI) {
892        self.state_mut()
893            .narrative
894            .push(Narrative::Problem(ProblemState::new(uri)));
895    }
896    fn close_problem(&mut self) -> Option<ProblemState> {
897        match self.state_mut().narrative.pop() {
898            Some(Narrative::Problem(state)) => return Some(state),
899            Some(o) => self.state_mut().narrative.push(o),
900            None => {}
901        }
902        None
903    }
904    fn add_precondition(&mut self, uri: SymbolURI, dim: CognitiveDimension) {
905        let e = self.state_mut().narrative.iter_mut().rev().find_map(|e| {
906            if let Narrative::Problem(e) = e {
907                Some(e)
908            } else {
909                None
910            }
911        });
912        if let Some(e) = e {
913            e.preconditions.push((dim, uri))
914        } else {
915            self.add_error(FTMLError::NotInNarrative);
916        }
917    }
918    fn add_objective(&mut self, uri: SymbolURI, dim: CognitiveDimension) {
919        let e = self.state_mut().narrative.iter_mut().rev().find_map(|e| {
920            if let Narrative::Problem(e) = e {
921                Some(e)
922            } else {
923                None
924            }
925        });
926        if let Some(e) = e {
927            e.objectives.push((dim, uri))
928        } else {
929            self.add_error(FTMLError::NotInNarrative);
930        }
931    }
932    fn open_decl(&mut self) {
933        self.state_mut()
934            .content
935            .push(Content::Symdecl { df: None, tp: None });
936    }
937    fn close_decl(&mut self) -> Option<(Option<Term>, Option<Term>)> {
938        match self.state_mut().content.pop() {
939            Some(Content::Symdecl { df, tp }) => return Some((tp, df)),
940            Some(o) => self.state_mut().content.push(o),
941            None => {}
942        }
943        None
944    }
945    fn open_notation(&mut self) {
946        self.state_mut()
947            .narrative
948            .push(Narrative::Notation(NotationState {
949                attribute_index: 0,
950                inner_index: 0,
951                is_text: false,
952                components: Box::default(),
953                op: None,
954            }));
955    }
956    fn close_notation(&mut self) -> Option<NotationState> {
957        match self.state_mut().narrative.pop() {
958            Some(Narrative::Notation(state)) => return Some(state),
959            Some(o) => self.state_mut().narrative.push(o),
960            None => {}
961        }
962        None
963    }
964    fn open_args(&mut self) {
965        self.state_mut()
966            .content
967            .push(Content::Args(Vec::new(), None));
968    }
969    fn close_args(&mut self) -> (Vec<Arg>, Option<Term>) {
970        match self.state_mut().content.pop() {
971            Some(Content::Args(args, head)) => {
972                let mut ret = Vec::new();
973                let mut iter = args.into_iter();
974                while let Some(Some((a, m))) = iter.next() {
975                    ret.push(match a.close(m) {
976                        Ok(a) => a,
977                        Err(a) => {
978                            self.add_error(FTMLError::IncompleteArgs(1));
979                            a
980                        }
981                    });
982                }
983                for e in iter {
984                    if e.is_some() {
985                        self.add_error(FTMLError::IncompleteArgs(2));
986                    }
987                }
988                return (ret, head);
989            }
990            Some(o) => self.state_mut().content.push(o),
991            None => {}
992        }
993        (Vec::new(), None)
994    }
995
996    fn get_narrative_uri(&self) -> NarrativeURI {
997        self.state()
998            .narrative
999            .iter()
1000            .rev()
1001            .find_map(|t| match t {
1002            Narrative::Container(uri,_) => Some(uri.as_narrative().owned()),
1003            Narrative::Paragraph(ParagraphState { uri, .. }) |
1004            Narrative::Problem(ProblemState { uri,.. }) |
1005            //Narrative::Slide{uri,..} |
1006            Narrative::Section{uri,..} => Some(uri.as_narrative().owned()),
1007            Narrative::Notation(_) | Narrative::Slide{..} => None
1008        })
1009            .unwrap_or_else(|| unreachable!())
1010    }
1011
1012    fn add_definiendum(&mut self, uri: SymbolURI) {
1013        for n in self.state_mut().narrative.iter_mut().rev() {
1014            if let Narrative::Paragraph(ParagraphState { fors, .. }) = n {
1015                fors.get_or_insert_mut(uri, || None);
1016                return;
1017            }
1018        }
1019        self.add_error(FTMLError::NotInNarrative);
1020    }
1021
1022    fn get_content_uri(&self) -> Option<&ModuleURI> {
1023        self.state().content.iter().rev().find_map(|t| match t {
1024            Content::Container(uri, _) => Some(uri),
1025            _ => None,
1026        })
1027    }
1028
1029    fn add_module(&mut self, module: OpenModule<Unchecked>) {
1030        self.state_mut().modules.push(module);
1031    }
1032
1033    #[inline]
1034    fn new_id(&mut self, prefix: Cow<'static, str>) -> Box<str> {
1035        self.state_mut().ids.new_id(prefix)
1036    }
1037
1038    fn in_notation(&self) -> bool {
1039        self.state()
1040            .narrative
1041            .iter()
1042            .rev()
1043            .any(|s| matches!(s, Narrative::Notation(_)))
1044    }
1045    fn in_term(&self) -> bool {
1046        self.state().in_term
1047    }
1048    fn set_in_term(&mut self, b: bool) {
1049        self.state_mut().in_term = b
1050    }
1051
1052    fn add_document_element(&mut self, elem: DocumentElement<Unchecked>) {
1053        for narr in self.state_mut().narrative.iter_mut().rev() {
1054            if let Narrative::Container(_, c) = narr {
1055                c.push(elem);
1056                return;
1057            }
1058            if let Narrative::Paragraph(ParagraphState { children, .. })
1059            | Narrative::Problem(ProblemState { children, .. })
1060            | Narrative::Section { children, .. }
1061            | Narrative::Slide { children, .. } = narr
1062            {
1063                children.push(elem);
1064                return;
1065            }
1066        }
1067        unreachable!()
1068    }
1069    fn add_title(&mut self, ttl: DocumentRange) -> Result<(), DocumentRange> {
1070        for narr in self.state_mut().narrative.iter_mut().rev() {
1071            if let Narrative::Paragraph(ParagraphState { title, .. })
1072            | Narrative::Problem(ProblemState { title, .. })
1073            | Narrative::Section { title, .. } = narr
1074            {
1075                *title = Some(ttl);
1076                return Ok(());
1077            }
1078        }
1079        Err(ttl)
1080    }
1081
1082    /// ### Errors
1083    fn add_content_element(
1084        &mut self,
1085        elem: OpenDeclaration<Unchecked>,
1086    ) -> Result<(), OpenDeclaration<Unchecked>> {
1087        for cont in self.state_mut().content.iter_mut().rev() {
1088            if let Content::Container(_, c) = cont {
1089                c.push(elem);
1090                return Ok(());
1091            }
1092        }
1093        Err(elem)
1094    }
1095    fn add_notation(
1096        &mut self,
1097        NotationSpec {
1098            components,
1099            attribute_index,
1100            inner_index,
1101            is_text,
1102        }: NotationSpec,
1103    ) -> Result<(), NotationSpec> {
1104        if let Some(Narrative::Notation(NotationState {
1105            components: comps,
1106            attribute_index: idx,
1107            inner_index: iidx,
1108            is_text: text,
1109            ..
1110        })) = self.state_mut().narrative.last_mut()
1111        {
1112            *comps = components;
1113            *iidx = inner_index;
1114            *idx = attribute_index;
1115            *text = is_text;
1116            Ok(())
1117        } else {
1118            Err(NotationSpec {
1119                attribute_index,
1120                inner_index,
1121                is_text,
1122                components,
1123            })
1124        }
1125    }
1126    fn add_op_notation(&mut self, op: OpNotation) -> Result<(), OpNotation> {
1127        if let Some(Narrative::Notation(NotationState { op: ops, .. })) =
1128            self.state_mut().narrative.last_mut()
1129        {
1130            *ops = Some(op);
1131            Ok(())
1132        } else {
1133            Err(op)
1134        }
1135    }
1136    fn add_type(&mut self, tm: Term) -> Result<(), Term> {
1137        match self.state_mut().content.last_mut() {
1138            Some(Content::Symdecl { tp, .. }) => *tp = Some(tm),
1139            _ => return Err(tm),
1140        }
1141        Ok(())
1142    }
1143    /// #### Errors
1144    fn add_term(&mut self, symbol: Option<SymbolURI>, tm: Term) -> Result<(), Term> {
1145        if symbol.is_none() {
1146            match self.state_mut().content.last_mut() {
1147                Some(Content::Symdecl { df, .. }) => {
1148                    *df = Some(tm);
1149                    return Ok(());
1150                }
1151                Some(Content::Args(_, o) | Content::SingleTerm(o)) => {
1152                    *o = Some(tm);
1153                    return Ok(());
1154                }
1155                _ => (),
1156            }
1157        }
1158        for e in self.state_mut().narrative.iter_mut().rev() {
1159            if let Narrative::Paragraph(ParagraphState { fors, .. }) = e {
1160                if let Some(symbol) = symbol {
1161                    fors.insert(symbol, Some(tm));
1162                    return Ok(());
1163                }
1164                if fors.0.len() == 1 {
1165                    fors.0.last_mut().unwrap_or_else(|| unreachable!()).1 = Some(tm);
1166                    return Ok(());
1167                }
1168            }
1169        }
1170        Err(tm)
1171    }
1172
1173    fn add_arg(
1174        &mut self,
1175        (idx, maybe_ls): (u8, Option<u8>),
1176        tm: Term,
1177        mode: ArgMode,
1178    ) -> Result<(), ()> {
1179        if let Some(Content::Args(v, _)) = self.state_mut().content.last_mut() {
1180            if v.len() <= idx as usize {
1181                v.resize(idx as usize + 1, None);
1182            }
1183            let tl = v
1184                .get_mut((idx - 1) as usize)
1185                .unwrap_or_else(|| unreachable!());
1186            if let Some(idx) = maybe_ls {
1187                if tl.is_none() {
1188                    *tl = Some((TermOrList::List(vec![]), mode));
1189                }
1190                if let Some((TermOrList::List(ls), _)) = tl {
1191                    if ls.len() <= idx as usize {
1192                        ls.resize(idx as usize + 1, None);
1193                    }
1194                    let tl = ls
1195                        .get_mut((idx - 1) as usize)
1196                        .unwrap_or_else(|| unreachable!());
1197                    *tl = Some(tm);
1198                } else {
1199                    return Err(());
1200                }
1201            } else {
1202                *tl = Some((TermOrList::Term(tm), mode));
1203            }
1204            Ok(())
1205        } else {
1206            Err(())
1207        }
1208    }
1209}