1use std::borrow::Cow;
4
5use super::ast;
6use crate::ds;
7
8pub trait ToBoxLang {
10 type Output;
11 fn to_box_lang(&self) -> Self::Output;
12}
13
14pub trait ToBoxworks {
16 type Output;
17 fn to_boxworks(&self) -> Self::Output;
18}
19
20impl ToBoxLang for ds::Vertical {
21 type Output = ast::Vertical<'static>;
22 fn to_box_lang(&self) -> Self::Output {
23 use crate::ds::Vertical::*;
24 match self {
25 HBox(hbox) => ast::Vertical::HBox(hbox.to_box_lang()),
26 VBox(vbox) => ast::Vertical::VBox(vbox.to_box_lang()),
27 Rule(rule) => ast::Vertical::Rule(rule.to_box_lang()),
28 Mark(mark) => ast::Vertical::Mark(mark.to_box_lang()),
29 Insertion(insertion) => ast::Vertical::Insertion(insertion.to_box_lang()),
30 Whatsit(_whatsit) => todo!(),
31 Math(math) => ast::Vertical::Math(math.to_box_lang()),
32 Glue(glue) => ast::Vertical::Glue(glue.to_box_lang()),
33 Kern(kern) => ast::Vertical::Kern(kern.to_box_lang()),
34 Penalty(penalty) => ast::Vertical::Penalty(penalty.to_box_lang()),
35 }
36 }
37}
38
39impl<'a> ToBoxworks for ast::Vertical<'a> {
40 type Output = ds::Vertical;
41
42 fn to_boxworks(&self) -> Self::Output {
43 use ast::Vertical::*;
44 match self {
45 HBox(hbox_args) => ds::Vertical::HBox(hbox_args.to_boxworks()),
46 VBox(vbox_args) => ds::Vertical::VBox(vbox_args.to_boxworks()),
47 Glue(glue_args) => ds::Vertical::Glue(glue_args.to_boxworks()),
48 Kern(kern_args) => ds::Vertical::Kern(kern_args.to_boxworks()),
49 Penalty(penalty_args) => ds::Vertical::Penalty(penalty_args.to_boxworks()),
50 Rule(rule_args) => ds::Vertical::Rule(rule_args.to_boxworks()),
51 Mark(mark_args) => ds::Vertical::Mark(mark_args.to_boxworks()),
52 Insertion(insertion_args) => ds::Vertical::Insertion(insertion_args.to_boxworks()),
53 Math(math_args) => ds::Vertical::Math(math_args.to_boxworks()),
54 }
55 }
56}
57
58impl ToBoxLang for Vec<ds::Horizontal> {
59 type Output = Vec<ast::Horizontal<'static>>;
60 fn to_box_lang(&self) -> Self::Output {
61 let mut out = vec![];
62 let mut current_font: Option<u32> = None;
63 let mut buf: String = Default::default();
64 let flush_chars = |out: &mut Vec<ast::Horizontal<'static>>,
65 current_font: &mut Option<u32>,
66 buf: &mut String| {
67 let Some(current_font) = current_font.take() else {
68 return;
70 };
71 let current_font: i32 = current_font.try_into().unwrap();
72 out.push(ast::Horizontal::Chars(ast::Chars {
73 content: Cow::<str>::Owned(buf.clone()).into(),
74 font: current_font.into(),
75 }));
76 buf.clear();
77 };
78 for elem in self {
79 match elem {
80 ds::Horizontal::Char(ds::Char { char, font }) => {
81 if Some(*font) != current_font {
82 flush_chars(&mut out, &mut current_font, &mut buf);
83 }
84 current_font = Some(*font);
85 buf.push(*char);
86 }
87 _ => {
88 flush_chars(&mut out, &mut current_font, &mut buf);
89 out.push(elem.to_box_lang());
90 }
91 }
92 }
93 flush_chars(&mut out, &mut current_font, &mut buf);
94 out
95 }
96}
97
98impl ToBoxLang for Vec<ds::Vertical> {
99 type Output = Vec<ast::Vertical<'static>>;
100 fn to_box_lang(&self) -> Self::Output {
101 self.iter().map(|b| b.to_box_lang()).collect()
102 }
103}
104
105impl ToBoxLang for Vec<ds::DiscretionaryElem> {
106 type Output = Vec<ast::DiscretionaryElem<'static>>;
107 fn to_box_lang(&self) -> Self::Output {
108 self.iter().map(|b| b.to_box_lang()).collect()
109 }
110}
111
112impl<'a> ToBoxworks for Vec<ast::Horizontal<'a>> {
113 type Output = Vec<ds::Horizontal>;
114 fn to_boxworks(&self) -> Self::Output {
115 self.iter().flat_map(|b| b.to_boxworks()).collect()
116 }
117}
118
119impl<'a> ToBoxworks for Vec<ast::Vertical<'a>> {
120 type Output = Vec<ds::Vertical>;
121 fn to_boxworks(&self) -> Self::Output {
122 self.iter().map(|b| b.to_boxworks()).collect()
123 }
124}
125
126impl<'a> ToBoxworks for Vec<ast::DiscretionaryElem<'a>> {
127 type Output = Vec<ds::DiscretionaryElem>;
128 fn to_boxworks(&self) -> Self::Output {
129 self.iter().flat_map(|b| b.to_boxworks()).collect()
130 }
131}
132
133impl<'a> ToBoxworks for ast::Horizontal<'a> {
134 type Output = Vec<ds::Horizontal>;
135
136 fn to_boxworks(&self) -> Self::Output {
137 use ast::Horizontal::*;
138 match self {
139 Chars(chars_args) => chars_args.to_boxworks(),
140 Glue(glue_args) => vec![ds::Horizontal::Glue(glue_args.to_boxworks())],
141 Kern(kern_args) => vec![ds::Horizontal::Kern(kern_args.to_boxworks())],
142 HBox(hbox_args) => vec![ds::Horizontal::HBox(hbox_args.to_boxworks())],
143 VBox(vbox_args) => vec![ds::Horizontal::VBox(vbox_args.to_boxworks())],
144 Ligature(lig_args) => vec![ds::Horizontal::Ligature(lig_args.to_boxworks())],
145 Discretionary(disc_args) => {
146 vec![ds::Horizontal::Discretionary(disc_args.to_boxworks())]
147 }
148 Rule(rule_args) => vec![ds::Horizontal::Rule(rule_args.to_boxworks())],
149 Penalty(penalty_args) => vec![ds::Horizontal::Penalty(penalty_args.to_boxworks())],
150 Mark(mark_args) => vec![ds::Horizontal::Mark(mark_args.to_boxworks())],
151 Adjust(adjust_args) => vec![ds::Horizontal::Adjust(adjust_args.to_boxworks())],
152 Insertion(insertion_args) => {
153 vec![ds::Horizontal::Insertion(insertion_args.to_boxworks())]
154 }
155 Math(math_args) => vec![ds::Horizontal::Math(math_args.to_boxworks())],
156 }
157 }
158}
159
160impl ToBoxLang for ds::Horizontal {
161 type Output = ast::Horizontal<'static>;
162 fn to_box_lang(&self) -> Self::Output {
163 use crate::ds::Horizontal::*;
164 match self {
165 Char(char) => ast::Horizontal::Chars(char.to_box_lang()),
166 HBox(hbox) => ast::Horizontal::HBox(hbox.to_box_lang()),
167 VBox(vbox) => ast::Horizontal::VBox(vbox.to_box_lang()),
168 Rule(rule) => ast::Horizontal::Rule(rule.to_box_lang()),
169 Mark(mark) => ast::Horizontal::Mark(mark.to_box_lang()),
170 Insertion(insertion) => ast::Horizontal::Insertion(insertion.to_box_lang()),
171 Adjust(adjust) => ast::Horizontal::Adjust(adjust.to_box_lang()),
172 Ligature(ligature) => ast::Horizontal::Ligature(ligature.to_box_lang()),
173 Discretionary(discretionary) => {
174 ast::Horizontal::Discretionary(discretionary.to_box_lang())
175 }
176 Whatsit(_whatsit) => todo!(),
177 Math(math) => ast::Horizontal::Math(math.to_box_lang()),
178 Glue(glue) => ast::Horizontal::Glue(glue.to_box_lang()),
179 Kern(kern) => ast::Horizontal::Kern(kern.to_box_lang()),
180 Penalty(penalty) => ast::Horizontal::Penalty(penalty.to_box_lang()),
181 }
182 }
183}
184
185impl<'a> ToBoxworks for ast::DiscretionaryElem<'a> {
186 type Output = Vec<ds::DiscretionaryElem>;
187
188 fn to_boxworks(&self) -> Self::Output {
189 use ast::DiscretionaryElem::*;
190 use ds::DiscretionaryElem as Out;
191 match self {
192 Chars(chars_args) => chars_to_discretionary_elems(chars_args),
193 Kern(kern_args) => vec![Out::Kern(kern_args.to_boxworks())],
194 HBox(hbox_args) => vec![Out::HBox(hbox_args.to_boxworks())],
195 VBox(vbox_args) => vec![Out::VBox(vbox_args.to_boxworks())],
196 Ligature(lig_args) => vec![Out::Ligature(lig_args.to_boxworks())],
197 Rule(rule_args) => vec![Out::Rule(rule_args.to_boxworks())],
198 }
199 }
200}
201
202impl ToBoxLang for ds::DiscretionaryElem {
203 type Output = ast::DiscretionaryElem<'static>;
204 fn to_box_lang(&self) -> Self::Output {
205 use crate::ds::DiscretionaryElem::*;
206 use ast::DiscretionaryElem as Out;
207 match self {
208 Char(char) => Out::Chars(char.to_box_lang()),
209 HBox(hbox) => Out::HBox(hbox.to_box_lang()),
210 VBox(vbox) => Out::VBox(vbox.to_box_lang()),
211 Rule(rule) => Out::Rule(rule.to_box_lang()),
212 Ligature(ligature) => Out::Ligature(ligature.to_box_lang()),
213 Kern(kern) => Out::Kern(kern.to_box_lang()),
214 }
215 }
216}
217
218impl ToBoxLang for ds::VBox {
219 type Output = ast::VBox<'static>;
220 fn to_box_lang(&self) -> Self::Output {
221 ast::VBox {
222 height: self.height.into(),
223 width: self.width.into(),
224 depth: self.depth.into(),
225 shift_amount: self.shift_amount.into(),
226 content: self.list.to_box_lang().into(),
227 }
228 }
229}
230
231impl ToBoxLang for ds::HBox {
232 type Output = ast::HBox<'static>;
233 fn to_box_lang(&self) -> Self::Output {
234 ast::HBox {
235 height: self.height.into(),
236 width: self.width.into(),
237 depth: self.depth.into(),
238 shift_amount: self.shift_amount.into(),
239 glue_ratio: self.glue_ratio.into(),
240 glue_order: self.glue_order.into(),
241 content: self.list.to_box_lang().into(),
242 }
243 }
244}
245
246impl<'a> ToBoxworks for ast::HBox<'a> {
247 type Output = ds::HBox;
248 fn to_boxworks(&self) -> Self::Output {
249 ds::HBox {
250 height: self.height.value,
251 width: self.width.value,
252 depth: self.depth.value,
253 shift_amount: self.shift_amount.value,
254 glue_ratio: self.glue_ratio.value,
255 glue_order: self.glue_order.value,
256 list: self.content.value.to_boxworks(),
257 }
258 }
259}
260
261impl<'a> ToBoxworks for ast::VBox<'a> {
262 type Output = ds::VBox;
263 fn to_boxworks(&self) -> Self::Output {
264 ds::VBox {
265 height: self.height.value,
266 width: self.width.value,
267 depth: self.depth.value,
268 shift_amount: self.shift_amount.value,
269 list: self.content.value.to_boxworks(),
270 ..Default::default()
271 }
272 }
273}
274
275impl<'a> ToBoxworks for ast::Ligature<'a> {
276 type Output = ds::Ligature;
277
278 fn to_boxworks(&self) -> Self::Output {
279 ds::Ligature {
280 included_left_boundary: false, included_right_boundary: false, char: self.char.value,
283 font: self.font.value as u32,
284 original_chars: self.original_chars.value.clone().into(),
285 }
286 }
287}
288
289impl ToBoxLang for ds::Ligature {
290 type Output = ast::Ligature<'static>;
291
292 fn to_box_lang(&self) -> Self::Output {
293 ast::Ligature {
294 char: self.char.into(),
295 original_chars: Cow::<'static, str>::Owned(format!["{}", self.original_chars]).into(),
296 font: (self.font as i32).into(),
297 }
298 }
299}
300
301impl ToBoxLang for ds::Char {
302 type Output = ast::Chars<'static>;
303 fn to_box_lang(&self) -> Self::Output {
304 ast::Chars {
305 content: Cow::<'static, str>::Owned(format!["{}", self.char]).into(),
306 font: (self.font as i32).into(),
307 }
308 }
309}
310
311impl<'a> ToBoxworks for ast::Chars<'a> {
312 type Output = Vec<ds::Horizontal>;
313 fn to_boxworks(&self) -> Self::Output {
314 self.content
315 .value
316 .chars()
317 .map(|c| {
318 ds::Horizontal::Char(ds::Char {
319 char: c,
320 font: self.font.value as u32,
321 })
322 })
323 .collect()
324 }
325}
326
327fn chars_to_discretionary_elems<'a>(chars: &ast::Chars<'a>) -> Vec<ds::DiscretionaryElem> {
328 chars
329 .content
330 .value
331 .chars()
332 .map(|c| {
333 ds::DiscretionaryElem::Char(ds::Char {
334 char: c,
335 font: chars.font.value as u32,
336 })
337 })
338 .collect()
339}
340
341impl ToBoxLang for ds::Penalty {
342 type Output = ast::Penalty<'static>;
343 fn to_box_lang(&self) -> Self::Output {
344 ast::Penalty {
345 value: self.0.into(),
346 }
347 }
348}
349
350impl<'a> ToBoxworks for ast::Penalty<'a> {
351 type Output = ds::Penalty;
352 fn to_boxworks(&self) -> Self::Output {
353 ds::Penalty(self.value.value)
354 }
355}
356
357impl ToBoxLang for ds::Glue {
358 type Output = ast::Glue<'static>;
359 fn to_box_lang(&self) -> Self::Output {
360 ast::Glue {
361 width: self.value.width.into(),
362 stretch: (self.value.stretch, self.value.stretch_order).into(),
363 shrink: (self.value.shrink, self.value.shrink_order).into(),
364 }
365 }
366}
367
368impl<'a> ToBoxworks for ast::Glue<'a> {
369 type Output = ds::Glue;
370 fn to_boxworks(&self) -> Self::Output {
371 ds::Glue {
372 value: common::Glue {
373 width: self.width.value,
374 stretch: self.stretch.value.0,
375 stretch_order: self.stretch.value.1,
376 shrink: self.shrink.value.0,
377 shrink_order: self.shrink.value.1,
378 },
379 kind: ds::GlueKind::Normal,
380 }
381 }
382}
383
384impl ToBoxLang for ds::Kern {
385 type Output = ast::Kern<'static>;
386 fn to_box_lang(&self) -> Self::Output {
387 ast::Kern {
388 width: self.width.into(),
389 }
390 }
391}
392
393impl<'a> ToBoxworks for ast::Kern<'a> {
394 type Output = ds::Kern;
395 fn to_boxworks(&self) -> Self::Output {
396 ds::Kern {
397 width: self.width.value,
398 kind: ds::KernKind::Normal,
399 }
400 }
401}
402
403impl ToBoxLang for ds::Discretionary {
404 type Output = ast::Discretionary<'static>;
405 fn to_box_lang(&self) -> Self::Output {
406 ast::Discretionary {
407 pre_break: self.pre_break.to_box_lang().into(),
408 post_break: self.post_break.to_box_lang().into(),
409 replace_count: (self.replace_count as i32).into(),
410 }
411 }
412}
413
414impl<'a> ToBoxworks for ast::Discretionary<'a> {
415 type Output = ds::Discretionary;
416 fn to_boxworks(&self) -> Self::Output {
417 ds::Discretionary {
418 pre_break: self.pre_break.value.to_boxworks(),
419 post_break: self.post_break.value.to_boxworks(),
420 replace_count: self.replace_count.value as u32,
421 }
422 }
423}
424
425impl ToBoxLang for ds::Rule {
426 type Output = ast::Rule<'static>;
427 fn to_box_lang(&self) -> Self::Output {
428 ast::Rule {
429 height: ast::MaybeRunning::from_scaled(self.height).into(),
430 width: ast::MaybeRunning::from_scaled(self.width).into(),
431 depth: ast::MaybeRunning::from_scaled(self.depth).into(),
432 }
433 }
434}
435
436impl<'a> ToBoxworks for ast::Rule<'a> {
437 type Output = ds::Rule;
438 fn to_boxworks(&self) -> Self::Output {
439 ds::Rule {
440 height: self.height.value.to_scaled(),
441 width: self.width.value.to_scaled(),
442 depth: self.depth.value.to_scaled(),
443 }
444 }
445}
446
447impl ToBoxLang for ds::Mark {
448 type Output = ast::Mark<'static>;
449 fn to_box_lang(&self) -> Self::Output {
450 ast::Mark::default()
451 }
452}
453
454impl<'a> ToBoxworks for ast::Mark<'a> {
455 type Output = ds::Mark;
456 fn to_boxworks(&self) -> Self::Output {
457 ds::Mark { list: vec![] }
458 }
459}
460
461impl ToBoxLang for ds::Adjust {
462 type Output = ast::Adjust<'static>;
463 fn to_box_lang(&self) -> Self::Output {
464 ast::Adjust {
465 content: self.list.to_box_lang().into(),
466 }
467 }
468}
469
470impl<'a> ToBoxworks for ast::Adjust<'a> {
471 type Output = ds::Adjust;
472 fn to_boxworks(&self) -> Self::Output {
473 ds::Adjust {
474 list: self.content.value.to_boxworks(),
475 }
476 }
477}
478
479impl ToBoxLang for ds::Insertion {
480 type Output = ast::Insertion<'static>;
481 fn to_box_lang(&self) -> Self::Output {
482 ast::Insertion {
483 box_number: (self.box_number as i32).into(),
484 height: self.height.into(),
485 split_max_depth: self.split_max_depth.into(),
486 split_top_skip_width: self.split_top_skip.width.into(),
487 split_top_skip_stretch: (
488 self.split_top_skip.stretch,
489 self.split_top_skip.stretch_order,
490 )
491 .into(),
492 split_top_skip_shrink: (self.split_top_skip.shrink, self.split_top_skip.shrink_order)
493 .into(),
494 float_penalty: (self.float_penalty as i32).into(),
495 vbox: self.vbox.to_box_lang().into(),
496 }
497 }
498}
499
500impl<'a> ToBoxworks for ast::Insertion<'a> {
501 type Output = ds::Insertion;
502 fn to_boxworks(&self) -> Self::Output {
503 ds::Insertion {
504 box_number: self.box_number.value as u8,
505 height: self.height.value,
506 split_max_depth: self.split_max_depth.value,
507 split_top_skip: common::Glue {
508 width: self.split_top_skip_width.value,
509 stretch: self.split_top_skip_stretch.value.0,
510 stretch_order: self.split_top_skip_stretch.value.1,
511 shrink: self.split_top_skip_shrink.value.0,
512 shrink_order: self.split_top_skip_shrink.value.1,
513 },
514 float_penalty: self.float_penalty.value as u32,
515 vbox: self.vbox.value.to_boxworks(),
516 }
517 }
518}
519
520impl ToBoxLang for ds::Math {
521 type Output = ast::Math<'static>;
522 fn to_box_lang(&self) -> Self::Output {
523 ast::Math {
524 kind: match self {
525 ds::Math::Before => Cow::Borrowed("before"),
526 ds::Math::After => Cow::Borrowed("after"),
527 }
528 .into(),
529 }
530 }
531}
532
533impl<'a> ToBoxworks for ast::Math<'a> {
534 type Output = ds::Math;
535 fn to_boxworks(&self) -> Self::Output {
536 match self.kind.value.as_ref() {
537 "after" => ds::Math::After,
538 _ => ds::Math::Before,
539 }
540 }
541}
542
543#[cfg(test)]
544mod tests {
545 use super::*;
546
547 macro_rules! tests {
548 ( $( ($name: ident, $input: expr, $want: expr,), )+ ) => {
549 $(
550 #[test]
551 fn $name() {
552 let input: Vec<ds::Horizontal> = $input;
553 let want: Vec<ast::Horizontal<'static>> = $want;
554 let got = input.to_box_lang();
555 assert_eq!(got, want);
556 }
557 )+
558 };
559 }
560
561 tests!(
562 (
563 chars_same_font,
564 vec![
565 ds::Char { char: 'B', font: 0 }.into(),
566 ds::Char { char: 'o', font: 0 }.into(),
567 ds::Char { char: 'x', font: 0 }.into(),
568 ],
569 vec![ast::Chars {
570 content: Cow::Borrowed("Box").into(),
571 font: 0_i32.into(),
572 }
573 .into()],
574 ),
575 (
576 chars_different_font,
577 vec![
578 ds::Char { char: 'B', font: 0 }.into(),
579 ds::Char { char: 'o', font: 0 }.into(),
580 ds::Char { char: 'x', font: 0 }.into(),
581 ds::Char { char: 'e', font: 1 }.into(),
582 ds::Char { char: 'd', font: 1 }.into(),
583 ],
584 vec![
585 ast::Chars {
586 content: Cow::Borrowed("Box").into(),
587 font: 0_i32.into(),
588 }
589 .into(),
590 ast::Chars {
591 content: Cow::Borrowed("ed").into(),
592 font: 1_i32.into(),
593 }
594 .into(),
595 ],
596 ),
597 );
598}