boxworks/lang/
ast.rs

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