1use super::cst::TreeIter;
5use super::ErrorAccumulator;
6
7use super::cst;
8use super::error::Error;
9use super::Str;
10use std::borrow::Cow;
11
12#[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#[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#[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
68pub 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
77pub 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 pub fn lower<'b>(&'b self) -> impl cst::TreeIter<'a> + 'b {
93 lower_hbox(std::slice::from_ref(self))
94 }
95 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 pub fn lower<'b>(&'b self) -> impl cst::TreeIter<'a> + 'b {
110 lower_vbox(std::slice::from_ref(self))
111 }
112 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 pub fn lower<'b>(&'b self) -> impl cst::TreeIter<'a> + 'b {
127 lower_dlist_impl(std::slice::from_ref(self))
128 }
129 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 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
263pub 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
272pub 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
291pub 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
310fn 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
526pub trait Func {
528 const NAME: &'static str;
530 const FIELD_NAMES: &'static [&'static str];
532 const DEFAULT_NUM_POS_ARG: usize = 0;
534}
535
536trait 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 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 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 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#[derive(Debug, Default, PartialEq, Eq, Clone)]
853pub struct Arg<'a, T> {
854 pub value: T,
856 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#[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#[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
959trait 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 value.lower(key)
1184}
1185
1186#[cfg(test)]
1187mod tests {
1188 use super::*;
1189 #[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}