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 #[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 pub trait PresenterArgs<W: std::fmt::Write> {
217 fn single(&self, idx: u8, mode: ArgMode, out: &mut W) -> Result;
219 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 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),..} => 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 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 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 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 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 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 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 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 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 term(out)?;
796 if insert_arg_attrs {
797 write!(out, "</mrow>")?;
798 }
799 i += 1;
800 }
801 Ok(()) }
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}