boxworks/lang/
ast.rs

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