boxworks_lang/
ast.rs

1//! Box language abstract syntax tree
2//!
3
4use crate::cst::TreeIter;
5use crate::ErrorAccumulator;
6
7use super::cst;
8use super::error::Error;
9use super::Str;
10use std::borrow::Cow;
11
12/// Element of a vertical list.
13///
14/// Corresponds to the [`boxworks::ds::Vertical`] type.
15#[derive(Debug, PartialEq, Eq, Clone)]
16#[allow(clippy::large_enum_variant)]
17pub enum Vertical<'a> {
18    Hlist(Hlist<'a>),
19    Vlist(Vlist<'a>),
20    Glue(Glue<'a>),
21    Kern(Kern<'a>),
22    Penalty(Penalty<'a>),
23    Rule(Rule<'a>),
24    Mark(Mark<'a>),
25    Insertion(Insertion<'a>),
26    Math(Math<'a>),
27}
28
29/// Element of a horizontal list.
30///
31/// Corresponds to the [`boxworks::ds::Horizontal`] type.
32#[derive(Debug, PartialEq, Eq, Clone)]
33pub enum Horizontal<'a> {
34    Chars(Chars<'a>),
35    Glue(Glue<'a>),
36    Penalty(Penalty<'a>),
37    Kern(Kern<'a>),
38    Hlist(Hlist<'a>),
39    Vlist(Vlist<'a>),
40    Ligature(Ligature<'a>),
41    Discretionary(Discretionary<'a>),
42    Rule(Rule<'a>),
43    Mark(Mark<'a>),
44    Adjust(Adjust<'a>),
45    Insertion(Insertion<'a>),
46    Math(Math<'a>),
47}
48
49impl<'a> From<Chars<'a>> for Horizontal<'a> {
50    fn from(value: Chars<'a>) -> Self {
51        Horizontal::Chars(value)
52    }
53}
54
55/// Element of a discretionary pre- or post-break list.
56///
57/// Corresponds to the [`boxworks::ds::DiscretionaryElem`] type.
58#[derive(Debug, PartialEq, Eq, Clone)]
59pub enum DiscretionaryElem<'a> {
60    Chars(Chars<'a>),
61    Kern(Kern<'a>),
62    Hlist(Hlist<'a>),
63    Vlist(Vlist<'a>),
64    Ligature(Ligature<'a>),
65    Rule(Rule<'a>),
66}
67
68/// Lower a horizontal list to a CST tree.
69pub fn lower_hlist<'a, 'b>(list: &'b [Horizontal<'a>]) -> impl cst::TreeIter<'a> + 'b {
70    lower_hlist_impl(list)
71}
72
73fn lower_hlist_impl<'a, 'b>(list: &'b [Horizontal<'a>]) -> CstTreeIter<'a, 'b> {
74    CstTreeIter::Hlist { list, next: 0 }
75}
76
77/// Lower a vertical list to a CST tree.
78pub fn lower_vlist<'a, 'b>(list: &'b [Vertical<'a>]) -> impl cst::TreeIter<'a> + 'b {
79    lower_vlist_impl(list)
80}
81
82fn lower_vlist_impl<'a, 'b>(list: &'b [Vertical<'a>]) -> CstTreeIter<'a, 'b> {
83    CstTreeIter::Vlist { list, next: 0 }
84}
85
86fn lower_dlist_impl<'a, 'b>(list: &'b [DiscretionaryElem<'a>]) -> CstTreeIter<'a, 'b> {
87    CstTreeIter::Dlist { list, next: 0 }
88}
89
90impl<'a> Horizontal<'a> {
91    /// Lower this element to a CST tree.
92    pub fn lower<'b>(&'b self) -> impl cst::TreeIter<'a> + 'b {
93        lower_hlist(std::slice::from_ref(self))
94    }
95    /// Lower the arguments of this element to a CST args iterator.
96    pub fn lower_args<'b>(&'b self) -> impl cst::ArgsIter<'a> + 'b {
97        self.lower_args_impl()
98    }
99    fn lower_args_impl<'b>(&'b self) -> CstArgsIter<'a, 'b> {
100        CstArgsIter::Hlist {
101            elem: self,
102            next: 0,
103        }
104    }
105}
106
107impl<'a> Vertical<'a> {
108    /// Lower this element to a CST tree.
109    pub fn lower<'b>(&'b self) -> impl cst::TreeIter<'a> + 'b {
110        lower_vlist(std::slice::from_ref(self))
111    }
112    /// Lower the arguments of this element to a CST args iterator.
113    pub fn lower_args<'b>(&'b self) -> impl cst::ArgsIter<'a> + 'b {
114        self.lower_args_impl()
115    }
116    fn lower_args_impl<'b>(&'b self) -> CstArgsIter<'a, 'b> {
117        CstArgsIter::Vlist {
118            elem: self,
119            next: 0,
120        }
121    }
122}
123
124impl<'a> DiscretionaryElem<'a> {
125    /// Lower this element to a CST tree.
126    pub fn lower<'b>(&'b self) -> impl cst::TreeIter<'a> + 'b {
127        lower_dlist_impl(std::slice::from_ref(self))
128    }
129    /// Lower the arguments of this element to a CST args iterator.
130    pub fn lower_args<'b>(&'b self) -> impl cst::ArgsIter<'a> + 'b {
131        self.lower_args_impl()
132    }
133    fn lower_args_impl<'b>(&'b self) -> CstArgsIter<'a, 'b> {
134        CstArgsIter::Dlist {
135            elem: self,
136            next: 0,
137        }
138    }
139}
140
141enum CstTreeIter<'a, 'b> {
142    Hlist {
143        list: &'b [Horizontal<'a>],
144        next: usize,
145    },
146    Vlist {
147        list: &'b [Vertical<'a>],
148        next: usize,
149    },
150    Dlist {
151        list: &'b [DiscretionaryElem<'a>],
152        next: usize,
153    },
154}
155
156enum CstArgsIter<'a, 'b> {
157    Hlist {
158        elem: &'b Horizontal<'a>,
159        next: usize,
160    },
161    Vlist {
162        elem: &'b Vertical<'a>,
163        next: usize,
164    },
165    Dlist {
166        elem: &'b DiscretionaryElem<'a>,
167        next: usize,
168    },
169}
170
171impl<'a, 'b> Iterator for CstTreeIter<'a, 'b> {
172    type Item = cst::TreeItem<'a, CstArgsIter<'a, 'b>>;
173
174    fn next(&mut self) -> Option<Self::Item> {
175        match self {
176            CstTreeIter::Hlist { list, next } => {
177                let h = list.get(*next)?;
178                *next += 1;
179                Some(cst::TreeItem::FuncCall {
180                    func_name: h.func_name().into(),
181                    args: h.lower_args_impl(),
182                })
183            }
184            CstTreeIter::Vlist { list, next } => {
185                let h = list.get(*next)?;
186                *next += 1;
187                Some(cst::TreeItem::FuncCall {
188                    func_name: h.func_name().into(),
189                    args: h.lower_args_impl(),
190                })
191            }
192            CstTreeIter::Dlist { list, next } => {
193                let h = list.get(*next)?;
194                *next += 1;
195                Some(cst::TreeItem::FuncCall {
196                    func_name: h.func_name().into(),
197                    args: h.lower_args_impl(),
198                })
199            }
200        }
201    }
202}
203
204impl<'a, 'b> cst::TreeIter<'a> for CstTreeIter<'a, 'b> {
205    type ArgsIter = CstArgsIter<'a, 'b>;
206    fn remaining_source(&self) -> Str<'a> {
207        "".into()
208    }
209}
210
211impl<'a, 'b> Iterator for CstArgsIter<'a, 'b> {
212    type Item = cst::ArgsItem<'a, CstTreeIter<'a, 'b>>;
213
214    fn next(&mut self) -> Option<Self::Item> {
215        match self {
216            CstArgsIter::Hlist { elem, next } => {
217                let l = elem.lower_arg(*next)?;
218                *next += 1;
219                Some(l)
220            }
221            CstArgsIter::Vlist { elem, next } => {
222                let l = elem.lower_arg(*next)?;
223                *next += 1;
224                Some(l)
225            }
226            CstArgsIter::Dlist { elem, next } => {
227                let l = elem.lower_arg(*next)?;
228                *next += 1;
229                Some(l)
230            }
231        }
232    }
233}
234
235impl<'a, 'b> cst::ArgsIter<'a> for CstArgsIter<'a, 'b> {
236    type TreeIter = CstTreeIter<'a, 'b>;
237}
238
239impl<'a> std::fmt::Display for Horizontal<'a> {
240    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241        cst::pretty_print(f, self.lower())
242    }
243}
244
245/// Parse Box language source code into a horizontal list.
246pub fn parse_hlist(source: &str) -> Result<Vec<Horizontal<'_>>, Vec<Error<'_>>> {
247    let errs: ErrorAccumulator = Default::default();
248    let calls = cst::parse(source, errs.clone());
249    let v = parse_hlist_using_cst(calls, &errs);
250    errs.check()?;
251    Ok(v)
252}
253
254/// Parse a hlist using an explicitly provided CST.
255pub fn parse_hlist_using_cst<'a>(
256    cst: impl cst::TreeIter<'a>,
257    errs: &ErrorAccumulator<'a>,
258) -> Vec<Horizontal<'a>> {
259    let mut v: Vec<Horizontal> = vec![];
260    for call in cst {
261        match call {
262            cst::TreeItem::FuncCall { func_name, args } => {
263                if let Some(elem) = convert_call_to_hlist_elem(func_name, args, errs) {
264                    v.push(elem);
265                }
266            }
267            cst::TreeItem::Comment { value: _ } => continue,
268        }
269    }
270    v
271}
272
273/// Parse a hlist using an explicitly provided CST.
274pub fn parse_vlist_using_cst<'a>(
275    cst: impl cst::TreeIter<'a>,
276    errs: &ErrorAccumulator<'a>,
277) -> Vec<Vertical<'a>> {
278    let mut v: Vec<Vertical> = vec![];
279    for call in cst {
280        match call {
281            cst::TreeItem::FuncCall { func_name, args } => {
282                if let Some(elem) = convert_call_to_vlist_elem(func_name, args, errs) {
283                    v.push(elem);
284                }
285            }
286            cst::TreeItem::Comment { value: _ } => continue,
287        }
288    }
289    v
290}
291
292/// Parse a dlist using an explicitly provided CST.
293fn parse_dlist_using_cst<'a>(
294    cst: impl cst::TreeIter<'a>,
295    errs: &ErrorAccumulator<'a>,
296) -> Vec<DiscretionaryElem<'a>> {
297    let mut v: Vec<DiscretionaryElem> = vec![];
298    for call in cst {
299        match call {
300            cst::TreeItem::FuncCall { func_name, args } => {
301                if let Some(elem) = convert_call_to_dlist_elem(func_name, args, errs) {
302                    v.push(elem);
303                }
304            }
305            cst::TreeItem::Comment { value: _ } => continue,
306        }
307    }
308    v
309}
310
311macro_rules! functions {
312    ( $( (
313        struct $name: ident <$lifetime: lifetime>  {
314            $(
315                $field_name: ident : $field_type: ty,
316            )+
317        }
318        impl Func {
319            func_name: $func_name: expr,
320            default_num_pos_arg: $default_num_pos_arg: expr,
321        }
322        $(
323            impl Horizontal {
324                variant: $horizontal_variant: ident,
325            }
326        )?
327        $(
328            impl Vertical {
329                variant: $vertical_variant: ident,
330            }
331        )?
332        $(
333            impl DiscretionaryElem {
334                variant: $discretionary_variant: ident,
335            }
336        )?
337    ), )+ ) => {
338        $(
339        #[derive(Debug, Default, PartialEq, Eq, Clone)]
340        pub struct $name <$lifetime> {
341            $(
342                pub $field_name : Arg<$lifetime, $field_type>,
343            )+
344        }
345        impl<$lifetime> Func for $name <$lifetime> {
346            const NAME: &'static str = "todo";
347            const FIELD_NAMES: &'static[&'static str] = &[ $( stringify!($field_name), )+];
348            const DEFAULT_NUM_POS_ARG: usize = $default_num_pos_arg;
349        }
350        impl<$lifetime> Args<$lifetime> for $name <$lifetime> {
351            fn assign_to_field<T: cst::TreeIter<$lifetime>>(&mut self, field_name: Str<$lifetime>, arg: cst::ArgsItem<$lifetime, T>, func_name: Str<$lifetime>, value_source: Str<$lifetime>,  errs: &ErrorAccumulator<$lifetime>)
352            {
353                match field_name.str() {
354                $(
355                    stringify!($field_name) => {
356                        self.$field_name.assign(arg, field_name.str(), func_name, value_source, errs);
357                    }
358                )+
359                    _ => {
360                        errs.add(Error::NoSuchArgument{function_name: func_name, argument: field_name });
361                    }
362                }
363            }
364            fn lower_arg<'b>(&'b self, u: usize) -> Option<cst::ArgsItem<'a, CstTreeIter<'a, 'b>>> {
365                let field_name = *Self::FIELD_NAMES.get(u)?;
366                match field_name {
367                $(
368                    stringify!($field_name) => {
369                        let key = if u < Self::DEFAULT_NUM_POS_ARG {
370                            None
371                        } else {
372                            Some(field_name.into())
373                        };
374                        Some(value_to_cst(&self.$field_name.value, key))
375                    }
376                )+
377                    _ => None,
378                }
379            }
380        }
381        )+
382        impl<'a> Horizontal<'a> {
383            pub fn func_name(&self) -> &'static str {
384                match self {
385                    $( $(
386                        Horizontal::$horizontal_variant(_) => $func_name,
387                    )? )+
388                }
389            }
390            pub fn field_names(&self) -> &'static [&'static str ] {
391                match self {
392                    $( $(
393                        Horizontal::$horizontal_variant(_) => $name::FIELD_NAMES,
394                    )? )+
395                }
396            }
397            fn lower_arg<'b>(&'b self, u: usize) -> Option<cst::ArgsItem<'a, CstTreeIter<'a, 'b>>> {
398                match self {
399                    $( $(
400                        Horizontal::$horizontal_variant(args) => args.lower_arg(u),
401                    )? )+
402                }
403            }
404        }
405        fn convert_call_to_hlist_elem<'a>(
406            func_name: Str<'a>,
407            call: impl cst::ArgsIter<'a>,
408            errs: &ErrorAccumulator<'a>,
409        ) -> Option<Horizontal<'a>> {
410            let h = match func_name.str() {
411                $( $(
412                    $func_name => Horizontal::$horizontal_variant($name::build(func_name, call, errs)?),
413                )? )+
414                _ => {
415                    errs.add(Error::NoSuchFunction {
416                        function_name: func_name,
417                    });
418                    return None;
419                }
420            };
421            Some(h)
422        }
423        impl<'a> DiscretionaryElem<'a> {
424            pub fn func_name(&self) -> &'static str {
425                match self {
426                    $( $(
427                        DiscretionaryElem::$discretionary_variant(_) => $func_name,
428                    )? )+
429                }
430            }
431            pub fn field_names(&self) -> &'static [&'static str ] {
432                match self {
433                    $( $(
434                        DiscretionaryElem::$discretionary_variant(_) => $name::FIELD_NAMES,
435                    )? )+
436                }
437            }
438            fn lower_arg<'b>(&'b self, u: usize) -> Option<cst::ArgsItem<'a, CstTreeIter<'a, 'b>>> {
439                match self {
440                    $( $(
441                        DiscretionaryElem::$discretionary_variant(args) => args.lower_arg(u),
442                    )? )+
443                }
444            }
445        }
446        fn convert_call_to_dlist_elem<'a>(
447            func_name: Str<'a>,
448            call: impl cst::ArgsIter<'a>,
449            errs: &ErrorAccumulator<'a>,
450        ) -> Option<DiscretionaryElem<'a>> {
451            let d = match func_name.str() {
452                $( $(
453                    $func_name => DiscretionaryElem::$discretionary_variant($name::build(func_name, call, errs)?),
454                )? )+
455                _ => {
456                    errs.add(Error::NoSuchFunction {
457                        function_name: func_name,
458                    });
459                    return None;
460                }
461            };
462            Some(d)
463        }
464        impl<'a> Vertical<'a> {
465            pub fn func_name(&self) -> &'static str {
466                match self {
467                    $( $(
468                        Vertical::$vertical_variant(_) => $func_name,
469                    )? )+
470                }
471            }
472            pub fn field_names(&self) -> &'static [&'static str ] {
473                match self {
474                    $( $(
475                        Vertical::$vertical_variant(_) => $name::FIELD_NAMES,
476                    )? )+
477                }
478            }
479            fn lower_arg<'b>(&'b self, u: usize) -> Option<cst::ArgsItem<'a, CstTreeIter<'a, 'b>>> {
480                match self {
481                    $( $(
482                        Vertical::$vertical_variant(args) => args.lower_arg(u),
483                    )? )+
484                }
485            }
486        }
487        fn convert_call_to_vlist_elem<'a>(
488            func_name: Str<'a>,
489            call: impl cst::ArgsIter<'a>,
490            errs: &ErrorAccumulator<'a>,
491        ) -> Option<Vertical<'a>> {
492            let h = match func_name.str() {
493                $( $(
494                    $func_name => Vertical::$vertical_variant($name::build(func_name, call, errs)?),
495                )? )+
496                _ => {
497                    errs.add(Error::NoSuchFunction {
498                        function_name: func_name,
499                    });
500                    return None;
501                }
502            };
503            Some(h)
504        }
505    };
506}
507
508/// A function like `text` or `glue`.
509pub trait Func {
510    /// Name of the function.
511    const NAME: &'static str;
512    /// Ordered list of field names.
513    const FIELD_NAMES: &'static [&'static str];
514    /// When printing, the number of arguments to print positionally.
515    const DEFAULT_NUM_POS_ARG: usize = 0;
516}
517
518/// Concrete strongly-type arguments to a function.
519trait Args<'a>: Func + Default {
520    fn assign_to_field<T: cst::TreeIter<'a>>(
521        &mut self,
522        field_name: Str<'a>,
523        arg: cst::ArgsItem<'a, T>,
524        func_name: Str<'a>,
525        value_source: Str<'a>,
526        errs: &ErrorAccumulator<'a>,
527    );
528
529    fn build(
530        func_name: Str<'a>,
531        args: impl cst::ArgsIter<'a>,
532        errs: &ErrorAccumulator<'a>,
533    ) -> Option<Self> {
534        let mut p: Self = Default::default();
535        let start = errs.len();
536        let mut field_names = Self::FIELD_NAMES.iter();
537        let mut last_keyword_arg: Option<Str> = None;
538        for arg in args {
539            let (key, value_source) = match &arg {
540                cst::ArgsItem::Regular {
541                    key,
542                    value: _,
543                    value_source,
544                } => (key, value_source.clone()),
545                cst::ArgsItem::List {
546                    key,
547                    square_open: _,
548                    tree,
549                } => (key, tree.remaining_source()),
550                cst::ArgsItem::Comment { .. } => continue,
551            };
552            let field_name = match key {
553                // Positional argument
554                None => {
555                    if let Some(keyword_arg) = &last_keyword_arg {
556                        errs.add(Error::PositionalArgAfterKeywordArg {
557                            positional_arg: value_source,
558                            keyword_arg: keyword_arg.clone(),
559                        });
560                        continue;
561                    }
562                    let Some(field_name) = field_names.next() else {
563                        errs.add(Error::TooManyPositionalArgs {
564                            extra_positional_arg: value_source,
565                            function_name: func_name.clone(),
566                            max_positional_args: Self::FIELD_NAMES.len(),
567                        });
568                        continue;
569                    };
570                    Str::new(field_name)
571                }
572                // Keyword argument
573                Some(field_name) => {
574                    last_keyword_arg = Some(Str {
575                        end: value_source.end,
576                        ..*field_name
577                    });
578                    field_name.clone()
579                }
580            };
581            p.assign_to_field(field_name, arg, func_name.clone(), value_source, errs);
582        }
583        if errs.len() == start {
584            Some(p)
585        } else {
586            None
587        }
588    }
589
590    /// Lower the ith argument.
591    fn lower_arg<'b>(&'b self, i: usize) -> Option<cst::ArgsItem<'a, CstTreeIter<'a, 'b>>>;
592}
593
594functions!(
595    (
596        struct Chars<'a> {
597            content: Cow<'a, str>,
598            font: i32,
599        }
600        impl Func {
601            func_name: "chars",
602            default_num_pos_arg: 1,
603        }
604        impl Horizontal {
605            variant: Chars,
606        }
607        impl DiscretionaryElem {
608            variant: Chars,
609        }
610    ),
611    (
612        struct Glue<'a> {
613            width: core::Scaled,
614            stretch: (core::Scaled, core::GlueOrder),
615            shrink: (core::Scaled, core::GlueOrder),
616        }
617        impl Func {
618            func_name: "glue",
619            default_num_pos_arg: 3,
620        }
621        impl Horizontal {
622            variant: Glue,
623        }
624        impl Vertical {
625            variant: Glue,
626        }
627    ),
628    (
629        struct Penalty<'a> {
630            value: i32,
631        }
632        impl Func {
633            func_name: "penalty",
634            default_num_pos_arg: 1,
635        }
636        impl Horizontal {
637            variant: Penalty,
638        }
639        impl Vertical {
640            variant: Penalty,
641        }
642    ),
643    (
644        struct Kern<'a> {
645            width: core::Scaled,
646        }
647        impl Func {
648            func_name: "kern",
649            default_num_pos_arg: 1,
650        }
651        impl Horizontal {
652            variant: Kern,
653        }
654        impl Vertical {
655            variant: Kern,
656        }
657        impl DiscretionaryElem {
658            variant: Kern,
659        }
660    ),
661    (
662        struct Hlist<'a> {
663            width: core::Scaled,
664            content: Vec<Horizontal<'a>>,
665        }
666        impl Func {
667            func_name: "hlist",
668            default_num_pos_arg: 0,
669        }
670        impl Horizontal {
671            variant: Hlist,
672        }
673        impl Vertical {
674            variant: Hlist,
675        }
676        impl DiscretionaryElem {
677            variant: Hlist,
678        }
679    ),
680    (
681        struct Ligature<'a> {
682            char: char,
683            original_chars: Cow<'a, str>,
684            font: i32,
685        }
686        impl Func {
687            func_name: "lig",
688            default_num_pos_arg: 2,
689        }
690        impl Horizontal {
691            variant: Ligature,
692        }
693        impl DiscretionaryElem {
694            variant: Ligature,
695        }
696    ),
697    (
698        struct Vlist<'a> {
699            content: Vec<Vertical<'a>>,
700        }
701        impl Func {
702            func_name: "vlist",
703            default_num_pos_arg: 0,
704        }
705        impl Horizontal {
706            variant: Vlist,
707        }
708        impl Vertical {
709            variant: Vlist,
710        }
711        impl DiscretionaryElem {
712            variant: Vlist,
713        }
714    ),
715    (
716        struct Discretionary<'a> {
717            pre_break: Vec<DiscretionaryElem<'a>>,
718            post_break: Vec<DiscretionaryElem<'a>>,
719            replace_count: i32,
720        }
721        impl Func {
722            func_name: "disc",
723            default_num_pos_arg: 0,
724        }
725        impl Horizontal {
726            variant: Discretionary,
727        }
728    ),
729    (
730        struct Rule<'a> {
731            height: core::Scaled,
732            width: core::Scaled,
733            depth: core::Scaled,
734        }
735        impl Func {
736            func_name: "rule",
737            default_num_pos_arg: 3,
738        }
739        impl Horizontal {
740            variant: Rule,
741        }
742        impl Vertical {
743            variant: Rule,
744        }
745        impl DiscretionaryElem {
746            variant: Rule,
747        }
748    ),
749    (
750        struct Mark<'a> {
751            dummy: i32,
752        }
753        impl Func {
754            func_name: "mark",
755            default_num_pos_arg: 0,
756        }
757        impl Horizontal {
758            variant: Mark,
759        }
760        impl Vertical {
761            variant: Mark,
762        }
763    ),
764    (
765        struct Adjust<'a> {
766            content: Vec<Vertical<'a>>,
767        }
768        impl Func {
769            func_name: "adjust",
770            default_num_pos_arg: 0,
771        }
772        impl Horizontal {
773            variant: Adjust,
774        }
775    ),
776    (
777        struct Insertion<'a> {
778            box_number: i32,
779            height: core::Scaled,
780            split_max_depth: core::Scaled,
781            split_top_skip_width: core::Scaled,
782            split_top_skip_stretch: (core::Scaled, core::GlueOrder),
783            split_top_skip_shrink: (core::Scaled, core::GlueOrder),
784            float_penalty: i32,
785            vlist: Vec<Vertical<'a>>,
786        }
787        impl Func {
788            func_name: "insertion",
789            default_num_pos_arg: 1,
790        }
791        impl Horizontal {
792            variant: Insertion,
793        }
794        impl Vertical {
795            variant: Insertion,
796        }
797    ),
798    (
799        struct Math<'a> {
800            kind: Cow<'a, str>,
801        }
802        impl Func {
803            func_name: "math",
804            default_num_pos_arg: 1,
805        }
806        impl Horizontal {
807            variant: Math,
808        }
809        impl Vertical {
810            variant: Math,
811        }
812    ),
813);
814
815/// An argument of type `T` to a function.
816#[derive(Debug, Default, PartialEq, Eq, Clone)]
817pub struct Arg<'a, T> {
818    /// Value of the argument.
819    pub value: T,
820    /// Source of the argument in Box source code.
821    ///
822    /// If this is [`None`], a value was not provided in source
823    ///     code and the default value is being used.
824    pub source: Option<Str<'a>>,
825}
826
827impl<'a, T> From<T> for Arg<'a, T> {
828    fn from(value: T) -> Self {
829        Arg {
830            value,
831            source: None,
832        }
833    }
834}
835
836// I think this private_bounds warning is a Rust bug?
837// All of the impl methods are private to this module so the bound
838// can't be seen from outside the module.
839#[allow(private_bounds)]
840impl<'a, T> Arg<'a, T>
841where
842    T: Value<'a>,
843{
844    fn assign<F: cst::TreeIter<'a>>(
845        &mut self,
846        arg: cst::ArgsItem<'a, F>,
847        field_name: &'a str,
848        func_name: Str<'a>,
849        value_source: Str<'a>,
850        errs: &ErrorAccumulator<'a>,
851    ) {
852        if let Some(first_assignment) = &self.source {
853            errs.add(Error::DuplicateArgument {
854                parameter_name: field_name,
855                first_assignment: first_assignment.clone(),
856                second_assignment: value_source,
857            });
858            return;
859        }
860        let (cast_result, value_type) = match arg {
861            cst::ArgsItem::Regular { value, .. } => match &value {
862                cst::Value::List(tree) => (T::try_cast_list(tree.iter(), errs), "a list"),
863                cst::Value::Integer(i) => (T::try_cast_integer(*i), "an integer"),
864                cst::Value::Scaled(scaled) => (T::try_cast_scaled(*scaled), "a number"),
865                cst::Value::InfiniteGlue(scaled, glue_order) => (
866                    T::try_cast_infinite_glue(*scaled, *glue_order),
867                    "an infinite glue",
868                ),
869                cst::Value::String(cow) => (T::try_cast_string(cow.clone()), "a string"),
870            },
871            cst::ArgsItem::List { tree, .. } => (T::try_cast_list(tree, errs), "a list"),
872            cst::ArgsItem::Comment { .. } => return,
873        };
874        match cast_result {
875            Some(val) => {
876                self.value = val;
877                self.source = Some(value_source);
878            }
879            None => errs.add(Error::IncorrectType {
880                wanted_type: T::DESCRIPTION,
881                got_type: value_type,
882                got_raw_value: value_source,
883                function_name: func_name,
884                parameter_name: field_name,
885            }),
886        }
887    }
888}
889
890/// Values in the AST.
891///
892/// These can possibly be obtained from a [`cst::Value`]
893///     and always lowered to a [`cst::Value`].
894trait Value<'a>: Sized {
895    const DESCRIPTION: &'static str;
896
897    fn try_cast_integer(_i: i32) -> Option<Self> {
898        None
899    }
900    fn try_cast_string(_s: Cow<'a, str>) -> Option<Self> {
901        None
902    }
903    fn try_cast_scaled(_s: core::Scaled) -> Option<Self> {
904        None
905    }
906    fn try_cast_infinite_glue(_s: core::Scaled, _o: core::GlueOrder) -> Option<Self> {
907        None
908    }
909    fn try_cast_list<F: cst::TreeIter<'a>>(
910        _value: F,
911        _errs: &ErrorAccumulator<'a>,
912    ) -> Option<Self> {
913        None
914    }
915
916    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>>;
917}
918
919impl<'a> Value<'a> for char {
920    const DESCRIPTION: &'static str = "a character";
921    fn try_cast_string(s: Cow<'a, str>) -> Option<Self> {
922        let mut iter = s.chars();
923        let c = iter.next()?;
924        match iter.next() {
925            Some(_) => None,
926            None => Some(c),
927        }
928    }
929    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
930        let s = format!("{self}");
931        cst::ArgsItem::Regular {
932            key,
933            value: cst::Value::String(s.into()),
934            value_source: "".into(),
935        }
936    }
937}
938
939impl<'a> Value<'a> for Cow<'a, str> {
940    const DESCRIPTION: &'static str = "a string";
941    fn try_cast_string(s: Cow<'a, str>) -> Option<Self> {
942        Some(s)
943    }
944    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
945        cst::ArgsItem::Regular {
946            key,
947            value: cst::Value::String(self.clone()),
948            value_source: "".into(),
949        }
950    }
951}
952
953impl<'a> Value<'a> for i32 {
954    const DESCRIPTION: &'static str = "an integer";
955    fn try_cast_integer(i: i32) -> Option<Self> {
956        Some(i)
957    }
958    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
959        cst::ArgsItem::Regular {
960            key,
961            value: cst::Value::Integer(*self),
962            value_source: "".into(),
963        }
964    }
965}
966
967impl<'a> Value<'a> for core::Scaled {
968    const DESCRIPTION: &'static str = "a number";
969    fn try_cast_scaled(s: core::Scaled) -> Option<Self> {
970        Some(s)
971    }
972    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
973        cst::ArgsItem::Regular {
974            key,
975            value: cst::Value::Scaled(*self),
976            value_source: "".into(),
977        }
978    }
979}
980
981impl<'a> Value<'a> for (core::Scaled, core::GlueOrder) {
982    const DESCRIPTION: &'static str = "a stretch or shrink glue component";
983    fn try_cast_scaled(s: core::Scaled) -> Option<Self> {
984        Some((s, core::GlueOrder::Normal))
985    }
986    fn try_cast_infinite_glue(s: core::Scaled, o: core::GlueOrder) -> Option<Self> {
987        Some((s, o))
988    }
989    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
990        cst::ArgsItem::Regular {
991            key,
992            value: cst::Value::InfiniteGlue(self.0, self.1),
993            value_source: "".into(),
994        }
995    }
996}
997
998impl<'a> Value<'a> for Vec<Vertical<'a>> {
999    const DESCRIPTION: &'static str = "a vlist";
1000    fn try_cast_list<F: cst::TreeIter<'a>>(value: F, errs: &ErrorAccumulator<'a>) -> Option<Self> {
1001        Some(parse_vlist_using_cst(value, errs))
1002    }
1003    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
1004        cst::ArgsItem::List {
1005            key,
1006            square_open: "".into(),
1007            tree: lower_vlist_impl(self),
1008        }
1009    }
1010}
1011
1012impl<'a> Value<'a> for Vec<Horizontal<'a>> {
1013    const DESCRIPTION: &'static str = "a hlist";
1014    fn try_cast_list<F: cst::TreeIter<'a>>(value: F, errs: &ErrorAccumulator<'a>) -> Option<Self> {
1015        Some(parse_hlist_using_cst(value, errs))
1016    }
1017    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
1018        cst::ArgsItem::List {
1019            key,
1020            square_open: "".into(),
1021            tree: lower_hlist_impl(self),
1022        }
1023    }
1024}
1025
1026impl<'a> Value<'a> for Vec<DiscretionaryElem<'a>> {
1027    const DESCRIPTION: &'static str = "a dlist";
1028    fn try_cast_list<F: cst::TreeIter<'a>>(value: F, errs: &ErrorAccumulator<'a>) -> Option<Self> {
1029        Some(parse_dlist_using_cst(value, errs))
1030    }
1031    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
1032        cst::ArgsItem::List {
1033            key,
1034            square_open: "".into(),
1035            tree: lower_dlist_impl(self),
1036        }
1037    }
1038}
1039
1040fn value_to_cst<'a, 'b, V: Value<'a>>(
1041    value: &'b V,
1042    key: Option<Str<'a>>,
1043) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
1044    // TODO: if the argument is positional and has its default value,
1045    // don't return anything.
1046    value.lower(key)
1047}
1048
1049#[cfg(test)]
1050mod tests {
1051    use super::*;
1052    /// FYI: most of the tests are doc tests.
1053    #[test]
1054    fn hlist() {
1055        let input = r#"
1056            hlist(
1057                width=1pt,
1058                content=[chars("Hello")],
1059            )
1060        "#;
1061
1062        let want = vec![Horizontal::Hlist(Hlist {
1063            width: Arg {
1064                value: core::Scaled::ONE.into(),
1065                source: Some("1pt".into()),
1066            },
1067            content: Arg {
1068                value: vec![Horizontal::Chars(Chars {
1069                    content: Arg {
1070                        value: "Hello".into(),
1071                        source: Some(r#""Hello""#.into()),
1072                    },
1073                    font: Arg {
1074                        value: 0,
1075                        source: None,
1076                    },
1077                    ..Default::default()
1078                })],
1079                source: Some(r#"[chars("Hello")]"#.into()),
1080            },
1081            ..Default::default()
1082        })];
1083
1084        let got = parse_hlist(&input).expect("parsing succeeds");
1085
1086        assert_eq!(got, want);
1087    }
1088}