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 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 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 #[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 fn add_notation(&mut self, spec: NotationSpec) -> Result<(), NotationSpec>;
122 fn add_op_notation(&mut self, op: OpNotation) -> Result<(), OpNotation>;
124 fn add_type(&mut self, tm: Term) -> Result<(), Term>;
126 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 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 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 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 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 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 #[inline]
221 fn get_language(&self, key: FTMLKey) -> Result<Language, FTMLError> {
222 self.get_typed(key, Language::from_str)
223 }
224
225 #[inline]
227 fn take_language(&mut self, key: FTMLKey) -> Result<Language, FTMLError> {
228 self.take_typed(key, Language::from_str)
229 }
230
231 #[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 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 #[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 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 #[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 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 #[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 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 #[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 #[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 #[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::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 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 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}