1use core::{Glue, Scaled};
2
3use super::keyword::parse_keyword;
4use crate::prelude as txl;
5use crate::token::Value;
6use crate::traits::*;
7use crate::*;
8
9impl Parsable for Glue {
10 fn parse_impl<S: TexlangState>(input: &mut vm::ExpandedStream<S>) -> txl::Result<Self> {
12 let negative = match super::integer::parse_optional_signs(input)? {
13 None => 1,
14 Some(_) => -1,
15 };
16 let first_token = input.next_or_err(GlueEndOfInputError {})?;
17 let width = match first_token.value() {
18 Value::CommandRef(command_ref) => {
19 use super::integer::InternalNumber;
20 match super::integer::parse_internal_number(input, first_token, command_ref)? {
21 InternalNumber::Integer(i) => {
22 super::dimen::scan_and_apply_units(
23 input,
24 first_token,
25 i.abs(),
26 Scaled::ZERO,
27 None,
28 )? * negative
29 * i.signum()
30 }
31 InternalNumber::Dimen(d) => d * negative,
32 InternalNumber::Glue(g) => {
33 return Ok(g * negative);
34 }
35 }
36 }
37 _ => {
38 input.back(first_token);
39 core::Scaled::parse(input)? * negative
40 }
41 };
42
43 let mut g = Glue {
44 width,
45 ..Default::default()
46 };
47 if parse_keyword(input, "plus")? {
48 g.stretch = super::dimen::scan_dimen(input, Some(&mut g.stretch_order))?
49 }
50 if parse_keyword(input, "minus")? {
51 g.shrink = super::dimen::scan_dimen(input, Some(&mut g.shrink_order))?
52 };
53 Ok(g)
54 }
55}
56
57#[derive(Debug)]
58struct GlueEndOfInputError;
59
60impl error::EndOfInputError for GlueEndOfInputError {
61 fn doing(&self) -> String {
62 "parsing a glue".into()
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use crate::parse::testing::*;
70 use core::GlueOrder;
71
72 #[derive(Default)]
73 struct State;
74
75 impl TexlangState for State {}
76
77 parse_success_tests![
78 (
79 width_1,
80 "0pt",
81 Glue {
82 ..Default::default()
83 }
84 ),
85 (
86 width_2,
87 "1pt",
88 Glue {
89 width: Scaled::ONE,
90 ..Default::default()
91 }
92 ),
93 (
94 width_3,
95 "-1pt",
96 Glue {
97 width: -Scaled::ONE,
98 ..Default::default()
99 }
100 ),
101 (
102 stretch_1,
103 "1pt plus 1pt",
104 Glue {
105 width: Scaled::ONE,
106 stretch: Scaled::ONE,
107 ..Default::default()
108 }
109 ),
110 (
111 stretch_fil,
112 "1pt plus 1fil",
113 Glue {
114 width: Scaled::ONE,
115 stretch: Scaled::ONE,
116 stretch_order: GlueOrder::Fil,
117 ..Default::default()
118 }
119 ),
120 (
121 stretch_fill,
122 "1pt plus 1fill",
123 Glue {
124 width: Scaled::ONE,
125 stretch: Scaled::ONE,
126 stretch_order: GlueOrder::Fill,
127 ..Default::default()
128 }
129 ),
130 (
131 stretch_filll,
132 "1pt plus 1filll",
133 Glue {
134 width: Scaled::ONE,
135 stretch: Scaled::ONE,
136 stretch_order: GlueOrder::Filll,
137 ..Default::default()
138 }
139 ),
140 ];
141
142 parse_failure_tests!(
143 Glue,
144 State,
145 (
146 stretch_overflow_1,
147 "1pt plus 30000000fil",
148 Glue {
149 width: Scaled::ONE,
150 stretch: Scaled::MAX_DIMEN,
151 stretch_order: GlueOrder::Fil,
152 ..Default::default()
153 }
154 ),
155 (
156 stretch_overflow_2,
157 "1pt plus -30000000fil",
158 Glue {
159 width: Scaled::ONE,
160 stretch: -Scaled::MAX_DIMEN,
161 stretch_order: GlueOrder::Fil,
162 ..Default::default()
163 }
164 ),
165 (
166 stretch_fillll,
167 "1pt plus 2fillll",
168 Glue {
169 width: Scaled::ONE,
170 stretch: Scaled::ONE * 2,
171 stretch_order: GlueOrder::Filll,
172 ..Default::default()
173 }
174 ),
175 );
176}