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
329macro_rules! functions {
330    ( $( (
331        struct $name: ident <$lifetime: lifetime>  {
332            $(
333                $field_name: ident : $field_type: ty,
334            )+
335        }
336        impl Func {
337            func_name: $func_name: expr,
338            default_num_pos_arg: $default_num_pos_arg: expr,
339        }
340        $(
341            impl Horizontal {
342                variant: $horizontal_variant: ident,
343            }
344        )?
345        $(
346            impl Vertical {
347                variant: $vertical_variant: ident,
348            }
349        )?
350        $(
351            impl DiscretionaryElem {
352                variant: $discretionary_variant: ident,
353            }
354        )?
355    ), )+ ) => {
356        $(
357        #[derive(Debug, Default, PartialEq, Eq, Clone)]
358        pub struct $name <$lifetime> {
359            $(
360                pub $field_name : Arg<$lifetime, $field_type>,
361            )+
362        }
363        impl<$lifetime> Func for $name <$lifetime> {
364            const NAME: &'static str = "todo";
365            const FIELD_NAMES: &'static[&'static str] = &[ $( stringify!($field_name), )+];
366            const DEFAULT_NUM_POS_ARG: usize = $default_num_pos_arg;
367        }
368        impl<$lifetime> Args<$lifetime> for $name <$lifetime> {
369            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>)
370            {
371                match field_name.str() {
372                $(
373                    stringify!($field_name) => {
374                        self.$field_name.assign(arg, field_name.str(), func_name, value_source, errs);
375                    }
376                )+
377                    _ => {
378                        errs.add(Error::NoSuchArgument{function_name: func_name, argument: field_name });
379                    }
380                }
381            }
382            fn lower_arg<'b>(&'b self, u: usize) -> Option<cst::ArgsItem<'a, CstTreeIter<'a, 'b>>> {
383                let field_name = *Self::FIELD_NAMES.get(u)?;
384                match field_name {
385                $(
386                    stringify!($field_name) => {
387                        let key = if u < Self::DEFAULT_NUM_POS_ARG {
388                            None
389                        } else {
390                            Some(field_name.into())
391                        };
392                        Some(value_to_cst(&self.$field_name.value, key))
393                    }
394                )+
395                    _ => None,
396                }
397            }
398        }
399        )+
400        impl<'a> Horizontal<'a> {
401            pub fn func_name(&self) -> &'static str {
402                match self {
403                    $( $(
404                        Horizontal::$horizontal_variant(_) => $func_name,
405                    )? )+
406                }
407            }
408            pub fn field_names(&self) -> &'static [&'static str ] {
409                match self {
410                    $( $(
411                        Horizontal::$horizontal_variant(_) => $name::FIELD_NAMES,
412                    )? )+
413                }
414            }
415            fn lower_arg<'b>(&'b self, u: usize) -> Option<cst::ArgsItem<'a, CstTreeIter<'a, 'b>>> {
416                match self {
417                    $( $(
418                        Horizontal::$horizontal_variant(args) => args.lower_arg(u),
419                    )? )+
420                }
421            }
422        }
423        fn convert_call_to_hbox_elem<'a>(
424            func_name: Str<'a>,
425            call: impl cst::ArgsIter<'a>,
426            errs: &ErrorAccumulator<'a>,
427        ) -> Option<Horizontal<'a>> {
428            let h = match func_name.str() {
429                $( $(
430                    $func_name => Horizontal::$horizontal_variant($name::build(func_name, call, errs)?),
431                )? )+
432                _ => {
433                    errs.add(Error::NoSuchFunction {
434                        function_name: func_name,
435                    });
436                    return None;
437                }
438            };
439            Some(h)
440        }
441        impl<'a> DiscretionaryElem<'a> {
442            pub fn func_name(&self) -> &'static str {
443                match self {
444                    $( $(
445                        DiscretionaryElem::$discretionary_variant(_) => $func_name,
446                    )? )+
447                }
448            }
449            pub fn field_names(&self) -> &'static [&'static str ] {
450                match self {
451                    $( $(
452                        DiscretionaryElem::$discretionary_variant(_) => $name::FIELD_NAMES,
453                    )? )+
454                }
455            }
456            fn lower_arg<'b>(&'b self, u: usize) -> Option<cst::ArgsItem<'a, CstTreeIter<'a, 'b>>> {
457                match self {
458                    $( $(
459                        DiscretionaryElem::$discretionary_variant(args) => args.lower_arg(u),
460                    )? )+
461                }
462            }
463        }
464        fn convert_call_to_dlist_elem<'a>(
465            func_name: Str<'a>,
466            call: impl cst::ArgsIter<'a>,
467            errs: &ErrorAccumulator<'a>,
468        ) -> Option<DiscretionaryElem<'a>> {
469            let d = match func_name.str() {
470                $( $(
471                    $func_name => DiscretionaryElem::$discretionary_variant($name::build(func_name, call, errs)?),
472                )? )+
473                _ => {
474                    errs.add(Error::NoSuchFunction {
475                        function_name: func_name,
476                    });
477                    return None;
478                }
479            };
480            Some(d)
481        }
482        impl<'a> Vertical<'a> {
483            pub fn func_name(&self) -> &'static str {
484                match self {
485                    $( $(
486                        Vertical::$vertical_variant(_) => $func_name,
487                    )? )+
488                }
489            }
490            pub fn field_names(&self) -> &'static [&'static str ] {
491                match self {
492                    $( $(
493                        Vertical::$vertical_variant(_) => $name::FIELD_NAMES,
494                    )? )+
495                }
496            }
497            fn lower_arg<'b>(&'b self, u: usize) -> Option<cst::ArgsItem<'a, CstTreeIter<'a, 'b>>> {
498                match self {
499                    $( $(
500                        Vertical::$vertical_variant(args) => args.lower_arg(u),
501                    )? )+
502                }
503            }
504        }
505        fn convert_call_to_vbox_elem<'a>(
506            func_name: Str<'a>,
507            call: impl cst::ArgsIter<'a>,
508            errs: &ErrorAccumulator<'a>,
509        ) -> Option<Vertical<'a>> {
510            let h = match func_name.str() {
511                $( $(
512                    $func_name => Vertical::$vertical_variant($name::build(func_name, call, errs)?),
513                )? )+
514                _ => {
515                    errs.add(Error::NoSuchFunction {
516                        function_name: func_name,
517                    });
518                    return None;
519                }
520            };
521            Some(h)
522        }
523    };
524}
525
526/// A function like `text` or `glue`.
527pub trait Func {
528    /// Name of the function.
529    const NAME: &'static str;
530    /// Ordered list of field names.
531    const FIELD_NAMES: &'static [&'static str];
532    /// When printing, the number of arguments to print positionally.
533    const DEFAULT_NUM_POS_ARG: usize = 0;
534}
535
536/// Concrete strongly-type arguments to a function.
537trait Args<'a>: Func + Default {
538    fn assign_to_field<T: cst::TreeIter<'a>>(
539        &mut self,
540        field_name: Str<'a>,
541        arg: cst::ArgsItem<'a, T>,
542        func_name: Str<'a>,
543        value_source: Str<'a>,
544        errs: &ErrorAccumulator<'a>,
545    );
546
547    fn build(
548        func_name: Str<'a>,
549        args: impl cst::ArgsIter<'a>,
550        errs: &ErrorAccumulator<'a>,
551    ) -> Option<Self> {
552        let mut p: Self = Default::default();
553        let start = errs.len();
554        let mut field_names = Self::FIELD_NAMES.iter();
555        let mut last_keyword_arg: Option<Str> = None;
556        for arg in args {
557            let (key, value_source) = match &arg {
558                cst::ArgsItem::Regular {
559                    key,
560                    value: _,
561                    value_source,
562                } => (key, value_source.clone()),
563                cst::ArgsItem::List {
564                    key,
565                    square_open: _,
566                    tree,
567                } => (key, tree.remaining_source()),
568                cst::ArgsItem::Comment { .. } => continue,
569            };
570            let field_name = match key {
571                // Positional argument
572                None => {
573                    if let Some(keyword_arg) = &last_keyword_arg {
574                        errs.add(Error::PositionalArgAfterKeywordArg {
575                            positional_arg: value_source,
576                            keyword_arg: keyword_arg.clone(),
577                        });
578                        continue;
579                    }
580                    let Some(field_name) = field_names.next() else {
581                        errs.add(Error::TooManyPositionalArgs {
582                            extra_positional_arg: value_source,
583                            function_name: func_name.clone(),
584                            max_positional_args: Self::FIELD_NAMES.len(),
585                        });
586                        continue;
587                    };
588                    Str::new(field_name)
589                }
590                // Keyword argument
591                Some(field_name) => {
592                    last_keyword_arg = Some(Str {
593                        end: value_source.end,
594                        ..*field_name
595                    });
596                    field_name.clone()
597                }
598            };
599            p.assign_to_field(field_name, arg, func_name.clone(), value_source, errs);
600        }
601        if errs.len() == start {
602            Some(p)
603        } else {
604            None
605        }
606    }
607
608    /// Lower the ith argument.
609    fn lower_arg<'b>(&'b self, i: usize) -> Option<cst::ArgsItem<'a, CstTreeIter<'a, 'b>>>;
610}
611
612functions!(
613    (
614        struct Chars<'a> {
615            content: Cow<'a, str>,
616            font: i32,
617        }
618        impl Func {
619            func_name: "chars",
620            default_num_pos_arg: 1,
621        }
622        impl Horizontal {
623            variant: Chars,
624        }
625        impl DiscretionaryElem {
626            variant: Chars,
627        }
628    ),
629    (
630        struct Glue<'a> {
631            width: common::Scaled,
632            stretch: (common::Scaled, common::GlueOrder),
633            shrink: (common::Scaled, common::GlueOrder),
634        }
635        impl Func {
636            func_name: "glue",
637            default_num_pos_arg: 3,
638        }
639        impl Horizontal {
640            variant: Glue,
641        }
642        impl Vertical {
643            variant: Glue,
644        }
645    ),
646    (
647        struct Penalty<'a> {
648            value: i32,
649        }
650        impl Func {
651            func_name: "penalty",
652            default_num_pos_arg: 1,
653        }
654        impl Horizontal {
655            variant: Penalty,
656        }
657        impl Vertical {
658            variant: Penalty,
659        }
660    ),
661    (
662        struct Kern<'a> {
663            width: common::Scaled,
664        }
665        impl Func {
666            func_name: "kern",
667            default_num_pos_arg: 1,
668        }
669        impl Horizontal {
670            variant: Kern,
671        }
672        impl Vertical {
673            variant: Kern,
674        }
675        impl DiscretionaryElem {
676            variant: Kern,
677        }
678    ),
679    (
680        struct HBox<'a> {
681            height: common::Scaled,
682            width: common::Scaled,
683            depth: common::Scaled,
684            shift_amount: common::Scaled,
685            glue_ratio: crate::ds::GlueRatio,
686            glue_order: common::GlueOrder,
687            content: Vec<Horizontal<'a>>,
688        }
689        impl Func {
690            func_name: "hbox",
691            default_num_pos_arg: 0,
692        }
693        impl Horizontal {
694            variant: HBox,
695        }
696        impl Vertical {
697            variant: HBox,
698        }
699        impl DiscretionaryElem {
700            variant: HBox,
701        }
702    ),
703    (
704        struct Ligature<'a> {
705            char: char,
706            original_chars: Cow<'a, str>,
707            font: i32,
708        }
709        impl Func {
710            func_name: "lig",
711            default_num_pos_arg: 2,
712        }
713        impl Horizontal {
714            variant: Ligature,
715        }
716        impl DiscretionaryElem {
717            variant: Ligature,
718        }
719    ),
720    (
721        struct VBox<'a> {
722            height: common::Scaled,
723            width: common::Scaled,
724            depth: common::Scaled,
725            shift_amount: common::Scaled,
726            content: Vec<Vertical<'a>>,
727        }
728        impl Func {
729            func_name: "vbox",
730            default_num_pos_arg: 0,
731        }
732        impl Horizontal {
733            variant: VBox,
734        }
735        impl Vertical {
736            variant: VBox,
737        }
738        impl DiscretionaryElem {
739            variant: VBox,
740        }
741    ),
742    (
743        struct Discretionary<'a> {
744            pre_break: Vec<DiscretionaryElem<'a>>,
745            post_break: Vec<DiscretionaryElem<'a>>,
746            replace_count: i32,
747        }
748        impl Func {
749            func_name: "disc",
750            default_num_pos_arg: 0,
751        }
752        impl Horizontal {
753            variant: Discretionary,
754        }
755    ),
756    (
757        struct Rule<'a> {
758            height: MaybeRunning,
759            width: MaybeRunning,
760            depth: MaybeRunning,
761        }
762        impl Func {
763            func_name: "rule",
764            default_num_pos_arg: 3,
765        }
766        impl Horizontal {
767            variant: Rule,
768        }
769        impl Vertical {
770            variant: Rule,
771        }
772        impl DiscretionaryElem {
773            variant: Rule,
774        }
775    ),
776    (
777        struct Mark<'a> {
778            dummy: i32,
779        }
780        impl Func {
781            func_name: "mark",
782            default_num_pos_arg: 0,
783        }
784        impl Horizontal {
785            variant: Mark,
786        }
787        impl Vertical {
788            variant: Mark,
789        }
790    ),
791    (
792        struct Adjust<'a> {
793            content: Vec<Vertical<'a>>,
794        }
795        impl Func {
796            func_name: "adjust",
797            default_num_pos_arg: 0,
798        }
799        impl Horizontal {
800            variant: Adjust,
801        }
802    ),
803    (
804        struct Insertion<'a> {
805            box_number: i32,
806            height: common::Scaled,
807            split_max_depth: common::Scaled,
808            split_top_skip_width: common::Scaled,
809            split_top_skip_stretch: (common::Scaled, common::GlueOrder),
810            split_top_skip_shrink: (common::Scaled, common::GlueOrder),
811            float_penalty: i32,
812            vbox: Vec<Vertical<'a>>,
813        }
814        impl Func {
815            func_name: "insertion",
816            default_num_pos_arg: 1,
817        }
818        impl Horizontal {
819            variant: Insertion,
820        }
821        impl Vertical {
822            variant: Insertion,
823        }
824    ),
825    (
826        struct Math<'a> {
827            kind: Cow<'a, str>,
828        }
829        impl Func {
830            func_name: "math",
831            default_num_pos_arg: 1,
832        }
833        impl Horizontal {
834            variant: Math,
835        }
836        impl Vertical {
837            variant: Math,
838        }
839    ),
840);
841
842impl<'a> std::fmt::Display for VBox<'a> {
843    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
844        let v_box: VBox<'a> = self.clone();
845        let tree = CstTreeIter::Other(&Horizontal::VBox(v_box));
846        cst::pretty_print(f, tree)?;
847        Ok(())
848    }
849}
850
851/// An argument of type `T` to a function.
852#[derive(Debug, Default, PartialEq, Eq, Clone)]
853pub struct Arg<'a, T> {
854    /// Value of the argument.
855    pub value: T,
856    /// Source of the argument in Box source code.
857    ///
858    /// If this is [`None`], a value was not provided in source
859    ///     code and the default value is being used.
860    pub source: Option<Str<'a>>,
861}
862
863impl<'a, T> From<T> for Arg<'a, T> {
864    fn from(value: T) -> Self {
865        Arg {
866            value,
867            source: None,
868        }
869    }
870}
871
872// I think this private_bounds warning is a Rust bug?
873// All of the impl methods are private to this module so the bound
874// can't be seen from outside the module.
875#[allow(private_bounds)]
876impl<'a, T> Arg<'a, T>
877where
878    T: Value<'a>,
879{
880    fn assign<F: cst::TreeIter<'a>>(
881        &mut self,
882        arg: cst::ArgsItem<'a, F>,
883        field_name: &'a str,
884        func_name: Str<'a>,
885        value_source: Str<'a>,
886        errs: &ErrorAccumulator<'a>,
887    ) {
888        if let Some(first_assignment) = &self.source {
889            errs.add(Error::DuplicateArgument {
890                parameter_name: field_name,
891                first_assignment: first_assignment.clone(),
892                second_assignment: value_source,
893            });
894            return;
895        }
896        let (cast_result, value_type) = match arg {
897            cst::ArgsItem::Regular { value, .. } => match &value {
898                cst::Value::List(tree) => (T::try_cast_list(tree.iter(), errs), "a list"),
899                cst::Value::Integer(i) => (T::try_cast_integer(*i), "an integer"),
900                cst::Value::Scaled(scaled) => (T::try_cast_scaled(*scaled), "a number"),
901                cst::Value::InfiniteGlue(scaled, glue_order) => (
902                    T::try_cast_infinite_glue(*scaled, *glue_order),
903                    "an infinite glue",
904                ),
905                cst::Value::String(cow) => (T::try_cast_string(cow.clone()), "a string"),
906            },
907            cst::ArgsItem::List { tree, .. } => (T::try_cast_list(tree, errs), "a list"),
908            cst::ArgsItem::Comment { .. } => return,
909        };
910        match cast_result {
911            Some(val) => {
912                self.value = val;
913                self.source = Some(value_source);
914            }
915            None => errs.add(Error::IncorrectType {
916                wanted_type: T::DESCRIPTION,
917                got_type: value_type,
918                got_raw_value: value_source,
919                function_name: func_name,
920                parameter_name: field_name,
921            }),
922        }
923    }
924}
925
926/// A rule dimension that is either a fixed length or running (i.e. determined by context).
927///
928/// Written as a dimension like `3pt` or as `"running"` for running in Box language.
929/// A running dimension corresponds to [`super::ds::Rule::RUNNING`].
930#[derive(Debug, PartialEq, Eq, Clone, Copy)]
931pub enum MaybeRunning {
932    Running,
933    Scaled(common::Scaled),
934}
935
936impl Default for MaybeRunning {
937    fn default() -> Self {
938        MaybeRunning::Scaled(common::Scaled::ZERO)
939    }
940}
941
942impl MaybeRunning {
943    pub fn to_scaled(self) -> common::Scaled {
944        match self {
945            MaybeRunning::Running => common::Scaled(i32::MIN),
946            MaybeRunning::Scaled(s) => s,
947        }
948    }
949
950    pub fn from_scaled(s: common::Scaled) -> Self {
951        if s.0 == i32::MIN {
952            MaybeRunning::Running
953        } else {
954            MaybeRunning::Scaled(s)
955        }
956    }
957}
958
959/// Values in the AST.
960///
961/// These can possibly be obtained from a [`cst::Value`]
962///     and always lowered to a [`cst::Value`].
963trait Value<'a>: Sized {
964    const DESCRIPTION: &'static str;
965
966    fn try_cast_integer(_i: i32) -> Option<Self> {
967        None
968    }
969    fn try_cast_string(_s: Cow<'a, str>) -> Option<Self> {
970        None
971    }
972    fn try_cast_scaled(_s: common::Scaled) -> Option<Self> {
973        None
974    }
975    fn try_cast_infinite_glue(_s: common::Scaled, _o: common::GlueOrder) -> Option<Self> {
976        None
977    }
978    fn try_cast_list<F: cst::TreeIter<'a>>(
979        _value: F,
980        _errs: &ErrorAccumulator<'a>,
981    ) -> Option<Self> {
982        None
983    }
984
985    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>>;
986}
987
988impl<'a> Value<'a> for common::GlueOrder {
989    const DESCRIPTION: &'static str = "a glue sign (normal, stretching or shrinking)";
990    fn try_cast_string(s: Cow<'a, str>) -> Option<Self> {
991        match s.as_ref() {
992            "normal" => Some(Self::Normal),
993            "fil" => Some(Self::Fil),
994            "fill" => Some(Self::Fill),
995            "filll" => Some(Self::Filll),
996            _ => None,
997        }
998    }
999    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
1000        use common::GlueOrder::*;
1001        cst::ArgsItem::Regular {
1002            key,
1003            value: cst::Value::String(
1004                match self {
1005                    Normal => "normal",
1006                    Fil => "fil",
1007                    Fill => "fill",
1008                    Filll => "filll",
1009                }
1010                .into(),
1011            ),
1012            value_source: "".into(),
1013        }
1014    }
1015}
1016
1017impl<'a> Value<'a> for crate::ds::GlueRatio {
1018    const DESCRIPTION: &'static str = "a glue ratio (floating point number)";
1019    fn try_cast_string(s: Cow<'a, str>) -> Option<Self> {
1020        crate::ds::GlueRatio::from_float_str(&s)
1021    }
1022    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
1023        cst::ArgsItem::Regular {
1024            key,
1025            value: cst::Value::String(format!["{}", self].into()),
1026            value_source: "".into(),
1027        }
1028    }
1029}
1030
1031impl<'a> Value<'a> for char {
1032    const DESCRIPTION: &'static str = "a character (i.e. a string containing single character)";
1033    fn try_cast_string(s: Cow<'a, str>) -> Option<Self> {
1034        let mut iter = s.chars();
1035        let c = iter.next()?;
1036        match iter.next() {
1037            Some(_) => None,
1038            None => Some(c),
1039        }
1040    }
1041    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
1042        let s = format!("{self}");
1043        cst::ArgsItem::Regular {
1044            key,
1045            value: cst::Value::String(s.into()),
1046            value_source: "".into(),
1047        }
1048    }
1049}
1050
1051impl<'a> Value<'a> for Cow<'a, str> {
1052    const DESCRIPTION: &'static str = "a string";
1053    fn try_cast_string(s: Cow<'a, str>) -> Option<Self> {
1054        Some(s)
1055    }
1056    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
1057        cst::ArgsItem::Regular {
1058            key,
1059            value: cst::Value::String(self.clone()),
1060            value_source: "".into(),
1061        }
1062    }
1063}
1064
1065impl<'a> Value<'a> for i32 {
1066    const DESCRIPTION: &'static str = "an integer";
1067    fn try_cast_integer(i: i32) -> Option<Self> {
1068        Some(i)
1069    }
1070    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
1071        cst::ArgsItem::Regular {
1072            key,
1073            value: cst::Value::Integer(*self),
1074            value_source: "".into(),
1075        }
1076    }
1077}
1078
1079impl<'a> Value<'a> for common::Scaled {
1080    const DESCRIPTION: &'static str = "a number";
1081    fn try_cast_scaled(s: common::Scaled) -> Option<Self> {
1082        Some(s)
1083    }
1084    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
1085        cst::ArgsItem::Regular {
1086            key,
1087            value: cst::Value::Scaled(*self),
1088            value_source: "".into(),
1089        }
1090    }
1091}
1092
1093impl<'a> Value<'a> for MaybeRunning {
1094    const DESCRIPTION: &'static str = "a dimension or *";
1095    fn try_cast_scaled(s: common::Scaled) -> Option<Self> {
1096        Some(MaybeRunning::Scaled(s))
1097    }
1098    fn try_cast_string(s: Cow<'a, str>) -> Option<Self> {
1099        if s == "running" {
1100            Some(MaybeRunning::Running)
1101        } else {
1102            None
1103        }
1104    }
1105    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
1106        let value = match self {
1107            MaybeRunning::Running => cst::Value::String("running".into()),
1108            MaybeRunning::Scaled(s) => cst::Value::Scaled(*s),
1109        };
1110        cst::ArgsItem::Regular {
1111            key,
1112            value,
1113            value_source: "".into(),
1114        }
1115    }
1116}
1117
1118impl<'a> Value<'a> for (common::Scaled, common::GlueOrder) {
1119    const DESCRIPTION: &'static str = "a stretch or shrink glue component";
1120    fn try_cast_scaled(s: common::Scaled) -> Option<Self> {
1121        Some((s, common::GlueOrder::Normal))
1122    }
1123    fn try_cast_infinite_glue(s: common::Scaled, o: common::GlueOrder) -> Option<Self> {
1124        Some((s, o))
1125    }
1126    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
1127        cst::ArgsItem::Regular {
1128            key,
1129            value: cst::Value::InfiniteGlue(self.0, self.1),
1130            value_source: "".into(),
1131        }
1132    }
1133}
1134
1135impl<'a> Value<'a> for Vec<Vertical<'a>> {
1136    const DESCRIPTION: &'static str = "a vbox";
1137    fn try_cast_list<F: cst::TreeIter<'a>>(value: F, errs: &ErrorAccumulator<'a>) -> Option<Self> {
1138        Some(parse_vbox_using_cst(value, errs))
1139    }
1140    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
1141        cst::ArgsItem::List {
1142            key,
1143            square_open: "".into(),
1144            tree: lower_vbox_impl(self),
1145        }
1146    }
1147}
1148
1149impl<'a> Value<'a> for Vec<Horizontal<'a>> {
1150    const DESCRIPTION: &'static str = "a hbox";
1151    fn try_cast_list<F: cst::TreeIter<'a>>(value: F, errs: &ErrorAccumulator<'a>) -> Option<Self> {
1152        Some(parse_hbox_using_cst(value, errs))
1153    }
1154    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
1155        cst::ArgsItem::List {
1156            key,
1157            square_open: "".into(),
1158            tree: lower_hbox_impl(self),
1159        }
1160    }
1161}
1162
1163impl<'a> Value<'a> for Vec<DiscretionaryElem<'a>> {
1164    const DESCRIPTION: &'static str = "a dlist";
1165    fn try_cast_list<F: cst::TreeIter<'a>>(value: F, errs: &ErrorAccumulator<'a>) -> Option<Self> {
1166        Some(parse_dlist_using_cst(value, errs))
1167    }
1168    fn lower<'b>(&'b self, key: Option<Str<'a>>) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
1169        cst::ArgsItem::List {
1170            key,
1171            square_open: "".into(),
1172            tree: lower_dlist_impl(self),
1173        }
1174    }
1175}
1176
1177fn value_to_cst<'a, 'b, V: Value<'a>>(
1178    value: &'b V,
1179    key: Option<Str<'a>>,
1180) -> cst::ArgsItem<'a, CstTreeIter<'a, 'b>> {
1181    // TODO: if the argument is positional and has its default value,
1182    // don't return anything.
1183    value.lower(key)
1184}
1185
1186#[cfg(test)]
1187mod tests {
1188    use super::*;
1189    /// FYI: most of the tests are doc tests.
1190    #[test]
1191    fn hbox() {
1192        let input = r#"
1193            hbox(
1194                width=1pt,
1195                content=[chars("Hello")],
1196            )
1197        "#;
1198
1199        let want = vec![Horizontal::HBox(HBox {
1200            width: Arg {
1201                value: common::Scaled::ONE.into(),
1202                source: Some("1pt".into()),
1203            },
1204            content: Arg {
1205                value: vec![Horizontal::Chars(Chars {
1206                    content: Arg {
1207                        value: "Hello".into(),
1208                        source: Some(r#""Hello""#.into()),
1209                    },
1210                    font: Arg {
1211                        value: 0,
1212                        source: None,
1213                    },
1214                    ..Default::default()
1215                })],
1216                source: Some(r#"[chars("Hello")]"#.into()),
1217            },
1218            ..Default::default()
1219        })];
1220
1221        let got = parse_hbox(&input).expect("parsing succeeds");
1222
1223        assert_eq!(got, want);
1224    }
1225}