flams_ontology/narration/
notations.rs

1use crate::{content::terms::ArgMode, ftml::FTMLKey, Resourcable};
2use smallvec::SmallVec;
3
4#[derive(Debug, Clone, PartialEq, Eq)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub struct Notation {
7    pub is_text: bool,
8    pub precedence: isize,
9    pub attribute_index: u8,
10    pub inner_index: u16,
11    pub id: Box<str>,
12    pub argprecs: SmallVec<isize, 9>,
13    pub components: Box<[NotationComponent]>,
14    pub op: Option<OpNotation>,
15}
16impl Resourcable for Notation {}
17impl Notation {
18    #[must_use]
19    pub fn is_op(&self) -> bool {
20        self.op.is_some()
21            || !self.components.iter().any(|c| {
22                matches!(
23                    c,
24                    NotationComponent::Arg(..)
25                        | NotationComponent::ArgMap { .. }
26                        | NotationComponent::ArgSep { .. }
27                )
28            })
29    }
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34pub struct OpNotation {
35    pub attribute_index: u8,
36    pub is_text: bool,
37    pub inner_index: u16,
38    pub text: Box<str>,
39}
40impl OpNotation {
41    pub fn display_ftml<'a>(
42        &'a self,
43        as_variable: bool,
44        uri: impl std::fmt::Display + 'a,
45    ) -> impl std::fmt::Display + 'a {
46        struct OpDisplayer<'n, U: std::fmt::Display + 'n> {
47            op: &'n OpNotation,
48            as_variable: bool,
49            uri: U,
50        }
51        impl<U: std::fmt::Display> std::fmt::Display for OpDisplayer<'_, U> {
52            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53                const TERM: &str = FTMLKey::Term.attr_name();
54                const HEAD: &str = FTMLKey::Head.attr_name();
55                const COMP: &str = FTMLKey::Comp.attr_name();
56                let tp = if self.as_variable { "OMV" } else { "OMID" };
57                let uri = &self.uri;
58                let text = &self.op.text;
59                write!(
60                    f,
61                    "<mrow {TERM}=\"{tp}\" {HEAD}=\"{uri}\" {COMP}>{text}</mrow>"
62                )
63            }
64        }
65        OpDisplayer {
66            op: self,
67            as_variable,
68            uri,
69        }
70    }
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
74#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
75pub enum NotationComponent {
76    S(Box<str>),
77    Arg(u8, ArgMode),
78    ArgSep {
79        index: u8,
80        mode: ArgMode,
81        sep: Box<[NotationComponent]>,
82    },
83    ArgMap {
84        index: u8,
85        segments: Box<[NotationComponent]>,
86    },
87    MainComp(Box<str>),
88    Comp(Box<str>),
89}
90
91pub use presentation::{PresentationError, Presenter, PresenterArgs};
92
93mod presentation {
94    use flams_utils::prelude::HMap;
95    use std::fmt::Display;
96
97    use crate::{
98        content::terms::{Arg, ArgMode, Informal, Term, Var},
99        ftml::FTMLKey,
100        omsp,
101        uris::{ContentURI, DocumentElementURI, SymbolURI, URITrait},
102    };
103
104    pub type Result = std::result::Result<(), PresentationError>;
105
106    #[derive(Debug)]
107    pub enum PresentationError {
108        Formatting,
109        MalformedNotation(String),
110        ArgumentMismatch,
111    }
112    impl From<std::fmt::Error> for PresentationError {
113        #[inline]
114        fn from(_: std::fmt::Error) -> Self {
115            Self::Formatting
116        }
117    }
118    impl std::fmt::Display for PresentationError {
119        #[inline]
120        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121            write!(f, "Presentation error: {self:?}")
122        }
123    }
124
125    pub trait Presenter: std::fmt::Write + Sized {
126        type N: AsRef<Notation>;
127        fn get_notation(&mut self, uri: &SymbolURI) -> Option<Self::N>;
128        fn get_op_notation(&mut self, uri: &SymbolURI) -> Option<Self::N>;
129        fn get_variable_notation(&mut self, uri: &DocumentElementURI) -> Option<Self::N>;
130        fn get_variable_op_notation(&mut self, uri: &DocumentElementURI) -> Option<Self::N>;
131        fn in_text(&self) -> bool;
132        /// #### Errors
133        #[inline]
134        fn cont(&mut self, tm: &Term) -> Result {
135            tm.present(self)
136        }
137    }
138
139    impl AsRef<Self> for Notation {
140        #[inline]
141        fn as_ref(&self) -> &Self {
142            self
143        }
144    }
145
146    struct FromStore<'s> {
147        out: String,
148        store: &'s NotationStore,
149    }
150    impl Presenter for FromStore<'_> {
151        type N = Notation;
152        #[inline]
153        fn get_notation(&mut self, uri: &SymbolURI) -> Option<Self::N> {
154            self.store
155                .notations
156                .get(uri)
157                .and_then(|v| v.first().cloned())
158        }
159        #[inline]
160        fn get_op_notation(&mut self, uri: &SymbolURI) -> Option<Self::N> {
161            self.store
162                .notations
163                .get(uri)
164                .and_then(|v| v.first().cloned())
165        }
166        #[inline]
167        fn get_variable_notation(&mut self, uri: &DocumentElementURI) -> Option<Self::N> {
168            self.store
169                .var_notations
170                .get(uri)
171                .and_then(|v| v.first().cloned())
172        }
173        #[inline]
174        fn get_variable_op_notation(&mut self, uri: &DocumentElementURI) -> Option<Self::N> {
175            self.store
176                .var_notations
177                .get(uri)
178                .and_then(|v| v.first().cloned())
179        }
180        #[inline]
181        fn in_text(&self) -> bool {
182            false
183        }
184        #[inline]
185        fn cont(&mut self, tm: &Term) -> Result {
186            tm.present(self)
187        }
188    }
189    impl std::fmt::Write for FromStore<'_> {
190        #[inline]
191        fn write_str(&mut self, s: &str) -> std::fmt::Result {
192            self.out.push_str(s);
193            Ok(())
194        }
195    }
196
197    #[derive(Debug, Clone, Default)]
198    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
199    pub struct NotationStore {
200        notations: HMap<SymbolURI, Vec<Notation>>,
201        var_notations: HMap<DocumentElementURI, Vec<Notation>>,
202    }
203    /*
204    impl NotationStore {
205        #[inline]
206        pub fn push(&mut self, uri:SymbolURI,notation:Notation) {
207            self.notations.entry(uri).or_default().push(notation);
208        }
209        #[inline]
210        pub fn push_var(&mut self, uri:DocumentElementURI,notation:Notation) {
211            self.var_notations.entry(uri).or_default().push(notation);
212        }
213    }
214     */
215
216    pub trait PresenterArgs<W: std::fmt::Write> {
217        /// #### Errors
218        fn single(&self, idx: u8, mode: ArgMode, out: &mut W) -> Result;
219        /// #### Errors
220        fn sequence(
221            &self,
222            idx: u8,
223            mode: ArgMode,
224        ) -> std::result::Result<
225            impl Iterator<Item = impl FnOnce(&mut W) -> Result>,
226            PresentationError,
227        >;
228    }
229
230    struct Displayer {
231        args: [char; 9],
232    }
233
234    const ARGS: &str = "abcdefghijk";
235    const VARS: &str = "xyzvwrstu";
236
237    impl Displayer {
238        fn new(n: &Notation) -> (Self, u8, &'static str) {
239            let mut args = ['_'; 9];
240            let mut vars = 0u8;
241            let mut arity = 0;
242            let mut vs = n
243                .components
244                .iter()
245                .filter_map(|c| match c {
246                    NotationComponent::Arg(i, m) => {
247                        arity = arity.max(*i);
248                        Some((*i, *m))
249                    }
250                    NotationComponent::ArgSep { index, mode, .. } => {
251                        arity = arity.max(*index);
252                        Some((*index, *mode))
253                    }
254                    NotationComponent::ArgMap { index, .. } => {
255                        arity = arity.max(*index);
256                        Some((*index, ArgMode::Normal))
257                    }
258                    _ => None,
259                })
260                .collect::<Vec<_>>();
261            vs.sort_by_key(|(i, _)| *i);
262            for (i, m) in vs {
263                if matches!(m, ArgMode::Binding | ArgMode::BindingSequence) {
264                    let var = vars as usize;
265                    vars += 1;
266                    args[(i - 1) as usize] =
267                        VARS.chars().nth(var).unwrap_or_else(|| unreachable!());
268                } else {
269                    let arg = ((i - 1) - vars) as usize;
270                    args[arg] = ARGS.chars().nth(arg).unwrap_or_else(|| unreachable!());
271                }
272            }
273            (
274                Self { args },
275                arity,
276                if vars > 0 { "OMBIND" } else { "OMA" },
277            )
278        }
279    }
280
281    struct NotationDisplay<'n, D: Display> {
282        d: Displayer,
283        notation: &'n Notation,
284        arity: u8,
285        termstr: &'static str,
286        uri: D,
287        op: bool,
288        omv: bool,
289    }
290
291    impl<D: Display> std::fmt::Display for NotationDisplay<'_, D> {
292        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293            let termstr = if self.arity == 0 || self.op {
294                if self.omv {
295                    "OMV"
296                } else {
297                    "OMID"
298                }
299            } else {
300                self.termstr
301            };
302            if self.op {
303                self.notation.apply_op(f, termstr, &self.uri, &self.d, true)
304            } else {
305                self.notation
306                    .apply_cont(f, None, termstr, &self.uri, true, &self.d)
307            }
308            .map_err(|_| std::fmt::Error)
309        }
310    }
311
312    impl<W: std::fmt::Write> PresenterArgs<W> for Displayer {
313        fn single(&self, idx: u8, _mode: ArgMode, out: &mut W) -> Result {
314            const TERM: &str = FTMLKey::Term.attr_name();
315            const HEAD: &str = FTMLKey::Head.attr_name();
316            const COMP: &str = FTMLKey::Comp.attr_name();
317
318            let c = self.args[(idx - 1) as usize];
319            write!(out, "<mi {TERM}=\"OMV\" {HEAD}=\"{c}\" {COMP}>{c}</mi>").map_err(Into::into)
320        }
321        fn sequence(
322            &self,
323            idx: u8,
324            _mode: ArgMode,
325        ) -> std::result::Result<
326            impl Iterator<Item = impl FnOnce(&mut W) -> Result>,
327            PresentationError,
328        > {
329            const TERM: &str = FTMLKey::Term.attr_name();
330            const HEAD: &str = FTMLKey::Head.attr_name();
331            const COMP: &str = FTMLKey::Comp.attr_name();
332
333            let c = self.args[(idx - 1) as usize];
334            Ok([
335                Some((c,'1')),
336                None,
337                Some((c,'n')),
338            ].map(|opt|
339                move |out:&mut W| if let Some((c,sub)) = opt {
340                    write!(out,"<msub {TERM}=\"OMV\" {HEAD}=\"{c}\" {COMP}><mi>{c}</mi><mi>{sub}</mi></mrow>")
341                } else {
342                    write!(out,"<mo>...</mo>")
343                }.map_err(Into::into)
344            ).into_iter())
345        }
346    }
347
348    impl<P: Presenter> PresenterArgs<P> for &'_ [Arg] {
349        fn single(&self, idx: u8, _mode: ArgMode, out: &mut P) -> Result {
350            let Some(arg) = self.get((idx - 1) as usize) else {
351                return Err(PresentationError::ArgumentMismatch);
352            };
353            out.cont(&arg.term)
354        }
355        fn sequence(
356            &self,
357            idx: u8,
358            _mode: ArgMode,
359        ) -> std::result::Result<
360            impl Iterator<Item = impl FnOnce(&mut P) -> Result>,
361            PresentationError,
362        > {
363            let Some(arg) = self.get((idx - 1) as usize) else {
364                return Err(PresentationError::ArgumentMismatch);
365            };
366            let args = arg
367                .term
368                .as_list()
369                .unwrap_or_else(|| std::array::from_ref(arg));
370            Ok(args.iter().map(|arg| move |p: &mut P| p.cont(&arg.term)))
371        }
372    }
373
374    use super::{Notation, NotationComponent};
375    impl Notation {
376        pub fn display_ftml<'a>(
377            &'a self,
378            op: bool,
379            as_variable: bool,
380            uri: &'a impl URITrait,
381        ) -> impl Display + 'a {
382            let (d, arity, termstr) = Displayer::new(self);
383            NotationDisplay {
384                d,
385                notation: self,
386                termstr,
387                op,
388                arity,
389                uri,
390                omv: as_variable,
391            }
392        }
393
394        #[inline]
395        fn default_omid(
396            out: &mut impl std::fmt::Write,
397            tp: &str,
398            uri: impl Display,
399            txt: &str,
400            in_text: bool,
401        ) -> Result {
402            const TERM: &str = FTMLKey::Term.attr_name();
403            const HEAD: &str = FTMLKey::Head.attr_name();
404            const COMP: &str = FTMLKey::Comp.attr_name();
405            if in_text {
406                write!(
407                    out,
408                    "<span {TERM}=\"{tp}\" {HEAD}=\"{uri}\" {COMP}>{txt}</span>"
409                )
410            } else {
411                write!(
412                    out,
413                    "<mtext {TERM}=\"{tp}\" {HEAD}=\"{uri}\" {COMP}>{txt}</mtext>"
414                )
415            }
416            .map_err(Into::into)
417        }
418
419        fn default_oma(
420            presenter: &mut impl Presenter,
421            tp: &str,
422            uri: impl Display,
423            txt: &str,
424            args: &[Arg],
425        ) -> Result {
426            const TERM: &str = FTMLKey::Term.attr_name();
427            const HEAD: &str = FTMLKey::Head.attr_name();
428            const COMP: &str = FTMLKey::Comp.attr_name();
429            const ARG: &str = FTMLKey::Arg.attr_name();
430            const MODE: &str = FTMLKey::ArgMode.attr_name();
431            if presenter.in_text() {
432                write!(
433                    presenter,
434                    "<span {TERM}=\"{tp}\" {HEAD}=\"{uri}\" {COMP}>{txt}</span>"
435                )
436                .map_err(Into::into)
437            } else {
438                write!(
439                    presenter,
440                    "<mrow><mtext {TERM}=\"{tp}\" {HEAD}=\"{uri}\" {COMP}>{txt}</mtext><mo>(</mo>"
441                )?;
442                let mut args = args.iter();
443                let Some(Arg { term, mode }) = args.next() else {
444                    return write!(presenter, "<mo>)</mo></mrow>").map_err(Into::into);
445                };
446                let mut idx = 1;
447                write!(presenter, "<mrow {ARG}=\"{idx}\" {MODE}=\"{mode}\">")?;
448                presenter.cont(term)?;
449                write!(presenter, "</mrow>")?;
450
451                for Arg { term, mode } in args {
452                    idx += 1;
453                    write!(presenter, "<mo>,</mo>")?;
454                    write!(presenter, "<mrow {ARG}=\"{idx}\" {MODE}=\"{mode}\">")?;
455                    presenter.cont(term)?;
456                    write!(presenter, "</mrow>")?;
457                }
458                write!(presenter, "<mo>)</mo></mrow>").map_err(Into::into)
459            }
460        }
461
462        /// #### Errors
463        pub(crate) fn present_term(term: &Term, presenter: &mut impl Presenter) -> Result {
464            match term {
465                omsp!(uri) =>
466                    if let Some(n) = presenter.get_op_notation(uri) {
467                        let args : &[Arg] = &[];
468                        n.as_ref().apply_op(presenter,"OMID",uri,&args,true)
469                    } else {
470                        Self::default_omid(presenter,"OMID",uri,uri.name().last_name().as_ref(),presenter.in_text())
471                    },
472                Term::OMV(Var::Ref{declaration:uri,is_sequence:_}) =>
473                    if let Some(n) = presenter.get_variable_notation(uri) {
474                        let args : &[Arg] = &[];
475                        n.as_ref().apply_op(presenter, "OMV",uri,&args,true)
476                    } else {
477                        Self::default_omid(presenter,"OMV",uri,uri.name().last_name().as_ref(),presenter.in_text())
478                    },
479                Term::OMV(Var::Name(name)) => Self::default_omid(presenter,"OMV",name,name.last_name().as_ref(),presenter.in_text()),
480                Term::Field { record, owner:Some(itm),..} => //box Term::OMID(ContentURI::Symbol(uri))),.. } =>
481                    match &**itm {
482                        Term::OMID(ContentURI::Symbol(uri)) =>
483                            if let Some(n) = presenter.get_op_notation(uri) {
484                                n.as_ref().apply_op_this(presenter,record,"COMPLEX",uri)
485                            } else {
486                                Self::default_omid(presenter,"OMID",uri,uri.name().last_name().as_ref(),presenter.in_text())
487                            },
488                        _ => write!(presenter,"<mtext>TODO: {term:?}</mtext>").map_err(Into::into)
489                    },
490                Term::OMA{head,args} => match &**head {
491                    Term::OMID(ContentURI::Symbol(uri)) =>
492                        if let Some(n) = presenter.get_notation(uri) {
493                            n.as_ref().apply(presenter,None,None,uri,args)
494                        } else {
495                            Self::default_oma(presenter, "OMA", uri, uri.name().last_name().as_ref(), args)
496                        },
497                    Term::OMV(Var::Ref{declaration:uri,is_sequence:_}) =>
498                        if let Some(n) = presenter.get_variable_notation(uri) {
499                            n.as_ref().apply(presenter,None,None,uri,args)
500                        } else {
501                            Self::default_oma(presenter, "OMA", uri, uri.name().last_name().as_ref(), args)
502                        }
503                    Term::OMV(Var::Name(name)) =>
504                        Self::default_oma(presenter, "OMA", name, name.last_name().as_ref(), args),
505                    _ => write!(presenter,"<mtext>TODO: {term:?}</mtext>").map_err(Into::into)
506
507                }
508                    /*
509                oma!(omsp!(uri),args)  =>
510                    if let Some(n) = presenter.get_notation(uri) {
511                        n.as_ref().apply(presenter,None,None,uri,args)
512                    } else {
513                        Self::default_oma(presenter, "OMA", uri, uri.name().last_name().as_ref(), args)
514                    },
515                Term::OMA{head:box Term::OMV(Var::Ref{declaration:uri,is_sequence:_}),args,..} =>
516                    if let Some(n) = presenter.get_variable_notation(uri) {
517                        n.as_ref().apply(presenter,None,None,uri,args)
518                    } else {
519                        Self::default_oma(presenter, "OMA", uri, uri.name().last_name().as_ref(), args)
520                    }
521                Term::OMA{head:box Term::OMV(Var::Name(name)),args,..} =>
522                    Self::default_oma(presenter, "OMA", name, name.last_name().as_ref(), args),
523                     */
524
525                Term::Informal { tag, attributes, children, terms,.. } =>
526                    Self::informal(presenter,tag,attributes,children,terms),
527                t => write!(presenter,"<mtext>TODO: {t:?}</mtext>").map_err(Into::into)
528            }
529        }
530
531        fn informal(
532            presenter: &mut impl Presenter,
533            tag: &str,
534            attributes: &[(Box<str>, Box<str>)],
535            children: &[Informal],
536            terms: &[Term],
537        ) -> Result {
538            fn has_terms(cs: &[Informal]) -> bool {
539                cs.iter().any(|c| match c {
540                    Informal::Term(_) => true,
541                    Informal::Text(_) => false,
542                    Informal::Node { children, .. } => has_terms(children),
543                })
544            }
545            write!(presenter, "<{tag}")?;
546            for (k, v) in attributes {
547                write!(presenter, " {k}=\"{v}\"")?;
548            }
549            if !has_terms(children) {
550                write!(presenter, " style=\"color:red\"")?;
551            }
552            write!(presenter, ">")?;
553            for c in children {
554                match c {
555                    Informal::Text(t) => write!(presenter, "{t}")?,
556                    Informal::Term(i) => {
557                        if let Some(t) = terms.get(*i as usize) {
558                            presenter.cont(t)?;
559                        } else {
560                            return Err(PresentationError::MalformedNotation(format!(
561                                "Term {i} not found in list {terms:?}"
562                            )));
563                        }
564                    }
565                    Informal::Node {
566                        tag,
567                        attributes,
568                        children,
569                    } => Self::informal(presenter, tag, attributes, children, terms)?,
570                }
571            }
572            write!(presenter, "</{tag}>").map_err(Into::into)
573        }
574
575        /// #### Errors
576        fn apply_op<W: std::fmt::Write>(
577            &self,
578            out: &mut W,
579            termstr: &str,
580            head: impl Display,
581            args: &impl PresenterArgs<W>,
582            insert_arg_attrs: bool,
583        ) -> Result {
584            const TERM: &str = FTMLKey::Term.attr_name();
585            const HEAD: &str = FTMLKey::Head.attr_name();
586            const NID: &str = FTMLKey::NotationId.attr_name();
587            if let Some(opn) = &self.op {
588                let index = opn.attribute_index as usize;
589                let start = &opn.text[0..index];
590                let end = &opn.text[index..];
591                write!(
592                    out,
593                    "{start} {TERM}=\"{termstr}\" {HEAD}=\"{head}\" {NID}=\"{}\"{end}",
594                    self.id
595                )
596                .map_err(Into::into)
597            } else {
598                self.apply_cont(out, None, termstr, head, insert_arg_attrs, args)
599            }
600        }
601        fn apply_op_this(
602            &self,
603            presenter: &mut impl Presenter,
604            this: &Term,
605            termstr: &str,
606            head: impl Display,
607        ) -> Result {
608            const TERM: &str = FTMLKey::Term.attr_name();
609            const HEAD: &str = FTMLKey::Head.attr_name();
610            const NID: &str = FTMLKey::NotationId.attr_name();
611            const HEADTERM: &str = FTMLKey::HeadTerm.attr_name();
612            if let Some(opn) = &self.op {
613                write!(
614                    presenter,
615                    "<msub {TERM}=\"{termstr}\" {HEAD}=\"{head}\" {NID}=\"{}\">{}",
616                    self.id, opn.text
617                )?;
618                write!(presenter, "<mrow {HEADTERM}>")?;
619                presenter.cont(this)?;
620                write!(presenter, "</mrow></msub>").map_err(Into::into)
621            } else {
622                self.apply(presenter, Some(this), Some(termstr), head, &[])
623            }
624        }
625
626        /// #### Errors
627        pub fn apply(
628            &self,
629            presenter: &mut impl Presenter,
630            this: Option<&Term>,
631            termstr: Option<&str>,
632            head: impl Display,
633            args: &[Arg],
634        ) -> Result {
635            // println!("Here: {:?} \n - {args:?}",self.components);
636            let termstr = termstr.unwrap_or_else(|| {
637                if args
638                    .iter()
639                    .any(|a| matches!(a.mode, ArgMode::Binding | ArgMode::BindingSequence))
640                {
641                    "OMBIND"
642                } else {
643                    "OMA"
644                }
645            });
646            self.apply_cont(presenter, this, termstr, head, true, &args)
647        }
648
649        /// #### Errors
650        pub fn apply_cont<W: std::fmt::Write>(
651            &self,
652            out: &mut W,
653            this: Option<&Term>,
654            termstr: &str,
655            head: impl Display,
656            insert_arg_attrs: bool,
657            args: &impl PresenterArgs<W>,
658        ) -> Result {
659            const TERM: &str = FTMLKey::Term.attr_name();
660            const HEAD: &str = FTMLKey::Head.attr_name();
661            const NID: &str = FTMLKey::NotationId.attr_name();
662            //println!("Components: {:?}",self.components);
663            let mut comps = self.components.iter();
664            match comps.next() {
665                Some(NotationComponent::S(start_node)) => {
666                    let index = self.attribute_index as usize;
667                    let start = &start_node[0..index];
668                    let end = &start_node[index..];
669                    write!(
670                        out,
671                        "{start} {TERM}=\"{termstr}\" {HEAD}=\"{head}\" {NID}=\"{}\"{end}",
672                        self.id
673                    )?;
674                    for comp in comps {
675                        comp.apply(out, this, args, false, insert_arg_attrs)?;
676                    }
677                    Ok(())
678                }
679                Some(o) => {
680                    write!(
681                        out,
682                        "<mrow {TERM}=\"{termstr}\" {HEAD}=\"{head}\" {NID}=\"{}\">",
683                        self.id
684                    )?;
685                    o.apply(out, this, args, false, insert_arg_attrs)?;
686                    for comp in comps {
687                        comp.apply(out, this, args, false, insert_arg_attrs)?;
688                    }
689                    write!(out, "</mrow>").map_err(Into::into)
690                }
691                _ => Ok(()),
692            }
693        }
694    }
695
696    impl NotationComponent {
697        fn apply<W: std::fmt::Write>(
698            &self,
699            out: &mut W,
700            this: Option<&Term>,
701            args: &impl PresenterArgs<W>,
702            in_text: bool,
703            insert_arg_attrs: bool,
704        ) -> Result {
705            match self {
706                Self::S(s) | Self::Comp(s) => out.write_str(s).map_err(Into::into),
707                Self::MainComp(s) => {
708                    if let Some(this) = this {
709                        Self::do_this(out, this, s)
710                    } else {
711                        out.write_str(s).map_err(Into::into)
712                    }
713                }
714                Self::Arg(idx, mode) => {
715                    Self::do_arg(out, *idx, args, *mode, in_text, insert_arg_attrs)
716                }
717                Self::ArgSep { index, mode, sep } => {
718                    Self::do_term_ls(out, *mode, *index, args, insert_arg_attrs, |p| {
719                        //println!("Separator: {sep:?}");
720                        for c in sep.iter().skip(1) {
721                            c.apply(p, this, args, in_text, insert_arg_attrs)?;
722                        }
723                        Ok(())
724                    })
725                }
726                t @ Self::ArgMap { .. } => {
727                    write!(out, "<mtext>TODO: {t:?}</mtext>").map_err(Into::into)
728                }
729            }
730        }
731
732        fn do_arg<W: std::fmt::Write>(
733            out: &mut W,
734            idx: u8,
735            args: &impl PresenterArgs<W>,
736            mode: ArgMode,
737            in_text: bool,
738            insert_arg_attrs: bool,
739        ) -> Result {
740            const ARG: &str = FTMLKey::Arg.attr_name();
741            const MODE: &str = FTMLKey::ArgMode.attr_name();
742            match mode {
743                ArgMode::Normal | ArgMode::Binding if !in_text => {
744                    if insert_arg_attrs {
745                        write!(out, "<mrow {ARG}=\"{idx}\" {MODE}=\"{mode}\">")?;
746                        args.single(idx, mode, out)?;
747                        write!(out, "</mrow>").map_err(Into::into)
748                    } else {
749                        args.single(idx, mode, out)
750                    }
751                }
752                ArgMode::Sequence | ArgMode::BindingSequence if !in_text => {
753                    Self::do_term_ls(out, mode, idx, args, insert_arg_attrs, |p| {
754                        write!(p, "<mo>,</mo>").map_err(Into::into)
755                    })
756                }
757                _ => write!(out, "<mtext>TODO: argument mode {mode:?}</mtext>").map_err(Into::into),
758            }
759        }
760
761        fn do_term_ls<W: std::fmt::Write>(
762            out: &mut W,
763            mode: ArgMode,
764            idx: u8,
765            args: &impl PresenterArgs<W>,
766            insert_arg_attrs: bool,
767            sep: impl Fn(&mut W) -> Result,
768        ) -> Result {
769            const ARG: &str = FTMLKey::Arg.attr_name();
770            const MODE: &str = FTMLKey::ArgMode.attr_name();
771            let mut ls = args.sequence(idx, mode)?;
772            let mode = match mode {
773                ArgMode::Sequence => ArgMode::Normal,
774                ArgMode::BindingSequence => ArgMode::Binding,
775                _ => unreachable!(),
776            };
777            let Some(first) = ls.next() else {
778                return Ok(());
779            };
780            if insert_arg_attrs {
781                write!(out, "<mrow {ARG}=\"{idx}1\" {MODE}=\"{mode}\">")?;
782            }
783            //println!("First {idx}{mode}: {first}");
784            first(out)?;
785            if insert_arg_attrs {
786                write!(out, "</mrow>")?;
787            }
788            let mut i = 2;
789            for term in ls {
790                sep(out)?;
791                if insert_arg_attrs {
792                    write!(out, "<mrow {ARG}=\"{idx}{i}\" {MODE}=\"{mode}\">")?;
793                }
794                //println!("term {i} of {idx}{mode}: {term}");
795                term(out)?;
796                if insert_arg_attrs {
797                    write!(out, "</mrow>")?;
798                }
799                i += 1;
800            }
801            Ok(()) //write!(presenter,"</mrow>").map_err(Into::into)
802        }
803
804        fn do_this<W: std::fmt::Write>(out: &mut W, _this: &Term, _main_comp: &str) -> Result {
805            write!(out, "<mtext>TODO: this</mtext>").map_err(Into::into)
806        }
807    }
808}