1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as Quoted;
3use quote::quote;
4use std::fmt::Debug;
5use std::ops::ControlFlow;
6use syn::{
7 parenthesized,
8 parse::{discouraged::Speculative, Parse, ParseStream, Result},
9 parse_macro_input, Error, Ident, LitStr,
10};
11
12fn _parsed<T: Parse>(input: ParseStream) -> Result<T> {
13 let _input = input.fork();
14 match _input.parse::<T>() {
15 Err(error) => Err(error),
16 Ok(result) => {
17 input.advance_to(&_input);
18 Ok(result)
19 }
20 }
21}
22
23fn _parse_any<T>(input: ParseStream, parsers: Vec<fn(ParseStream) -> Result<T>>) -> Result<T> {
24 let result = parsers.iter().try_fold(Vec::new(), |mut errors, parser| {
25 let _input = input.fork();
26 match parser(&_input) {
27 Ok(value) => {
28 input.advance_to(&_input);
29 ControlFlow::Break(value)
30 }
31 Err(new_error) => {
32 errors.push(new_error);
33 ControlFlow::Continue(errors)
34 }
35 }
36 });
37 match result {
38 ControlFlow::Break(value) => Ok(value),
39 ControlFlow::Continue(errors) => {
40 let error = Error::new(input.span(), "Failed to parse any");
41 Err(errors
42 .iter()
43 .cloned()
44 .fold(error, |mut out_error, in_error| {
45 out_error.combine(in_error);
46 out_error
47 }))
48 }
49 }
50}
51
52fn _parse_group<T>(input: ParseStream, parser: fn(ParseStream) -> Result<T>) -> Result<T> {
53 let content;
54 parenthesized!(content in input);
55 parser(&content)
56}
57
58#[derive(Debug, Clone)]
59enum UnaryOp {
60 Fix,
61 Grp,
62 Seq,
63 Nest,
64 Pack,
65}
66
67fn _parse_unary_op(input: ParseStream) -> Result<UnaryOp> {
68 let item: Ident = input.parse()?;
69 match item.to_string().as_str() {
70 "fix" => Ok(UnaryOp::Fix),
71 "grp" => Ok(UnaryOp::Grp),
72 "seq" => Ok(UnaryOp::Seq),
73 "nest" => Ok(UnaryOp::Nest),
74 "pack" => Ok(UnaryOp::Pack),
75 _ => Err(Error::new(item.span(), "Expected a unary operator")),
76 }
77}
78
79mod binary_tokens {
80 use syn::custom_punctuation;
81 custom_punctuation!(Unpadded, &);
82 custom_punctuation!(Padded, +);
83 custom_punctuation!(FixedUnpadded, !&);
84 custom_punctuation!(FixedPadded, !+);
85 custom_punctuation!(Newline, @);
86 custom_punctuation!(DoubleNewline, @@);
87}
88
89#[derive(Debug, Clone)]
90enum BinaryOp {
91 Unpadded,
92 Padded,
93 FixedUnpadded,
94 FixedPadded,
95 Newline,
96 DoubleNewline,
97}
98
99fn _parse_binary_op(input: ParseStream) -> Result<BinaryOp> {
100 use binary_tokens::*;
101 _parse_any(
102 input,
103 vec![
104 |input| _parsed::<Unpadded>(input).map(|_| BinaryOp::Unpadded),
105 |input| _parsed::<Padded>(input).map(|_| BinaryOp::Padded),
106 |input| _parsed::<FixedUnpadded>(input).map(|_| BinaryOp::FixedUnpadded),
107 |input| _parsed::<FixedPadded>(input).map(|_| BinaryOp::FixedPadded),
108 |input| _parsed::<DoubleNewline>(input).map(|_| BinaryOp::DoubleNewline),
109 |input| _parsed::<Newline>(input).map(|_| BinaryOp::Newline),
110 ],
111 )
112}
113
114#[derive(Debug, Clone)]
115enum Ast {
116 Null,
117 Variable(Ident),
118 Text(String),
119 Unary(UnaryOp, Box<Ast>),
120 Binary(BinaryOp, Box<Ast>, Box<Ast>),
121}
122
123fn _parse_null(input: ParseStream) -> Result<Box<Ast>> {
124 let item: Ident = input.parse()?;
125 match item.to_string().as_str() {
126 "null" => Ok(Box::new(Ast::Null)),
127 _ => Err(Error::new(item.span(), "Expected a unary operator")),
128 }
129}
130
131fn _parse_variable(input: ParseStream) -> Result<Box<Ast>> {
132 let name = _parsed::<Ident>(input)?;
133 Ok(Box::new(Ast::Variable(name)))
134}
135
136fn _parse_text(input: ParseStream) -> Result<Box<Ast>> {
137 let data = _parsed::<LitStr>(input)?;
138 Ok(Box::new(Ast::Text(data.value())))
139}
140
141fn _parse_group_ast(input: ParseStream) -> Result<Box<Ast>> {
142 _parse_group(input, _parse_ast)
143}
144
145fn _parse_primary(input: ParseStream) -> Result<Box<Ast>> {
146 _parse_any(
147 input,
148 vec![_parse_null, _parse_variable, _parse_text, _parse_group_ast],
149 )
150}
151
152fn _parse_atom(input: ParseStream) -> Result<Box<Ast>> {
153 _parse_any(input, vec![_parse_unary, _parse_primary])
154}
155
156fn _parse_unary(input: ParseStream) -> Result<Box<Ast>> {
157 let op = _parse_unary_op(input)?;
158 let ast = _parse_primary(input)?;
159 Ok(Box::new(Ast::Unary(op, ast)))
160}
161
162fn _parse_binary(input: ParseStream) -> Result<Box<Ast>> {
163 let left = _parse_atom(input)?;
164 let op = _parse_binary_op(input)?;
165 let right = _parse_ast(input)?;
166 Ok(Box::new(Ast::Binary(op, left, right)))
167}
168
169fn _parse_ast(input: ParseStream) -> Result<Box<Ast>> {
170 _parse_any(input, vec![_parse_binary, _parse_atom])
171}
172
173impl Parse for Box<Ast> {
174 fn parse(input: ParseStream) -> Result<Self> {
175 let _input = input.fork();
176 match _parse_ast(&_input) {
177 Err(error) => Err(error),
178 Ok(result) => {
179 input.advance_to(&_input);
180 if input.is_empty() {
181 Ok(result)
182 } else {
183 Err(Error::new(
184 input.span(),
185 format!("Failed to parse layout:\n{}", input),
186 ))
187 }
188 }
189 }
190 }
191}
192
193fn _reify_layout(ast: Ast) -> Quoted {
194 match ast {
195 Ast::Null => quote! { typeset::null() },
196 Ast::Variable(name) => quote! { #name.clone() },
197 Ast::Text(data) => quote! { typeset::text(#data.to_string()) },
198 Ast::Unary(UnaryOp::Fix, ast1) => {
199 let layout = _reify_layout(*ast1);
200 quote! { typeset::fix(#layout) }
201 }
202 Ast::Unary(UnaryOp::Grp, ast1) => {
203 let layout = _reify_layout(*ast1);
204 quote! { typeset::grp(#layout) }
205 }
206 Ast::Unary(UnaryOp::Seq, ast1) => {
207 let layout = _reify_layout(*ast1);
208 quote! { typeset::seq(#layout) }
209 }
210 Ast::Unary(UnaryOp::Nest, ast1) => {
211 let layout = _reify_layout(*ast1);
212 quote! { typeset::nest(#layout) }
213 }
214 Ast::Unary(UnaryOp::Pack, ast1) => {
215 let layout = _reify_layout(*ast1);
216 quote! { typeset::pack(#layout) }
217 }
218 Ast::Binary(BinaryOp::Unpadded, left, right) => {
219 let left_layout = _reify_layout(*left);
220 let right_layout = _reify_layout(*right);
221 quote! {
222 typeset::comp(
223 #left_layout,
224 #right_layout,
225 false,
226 false
227 )
228 }
229 }
230 Ast::Binary(BinaryOp::Padded, left, right) => {
231 let left_layout = _reify_layout(*left);
232 let right_layout = _reify_layout(*right);
233 quote! {
234 typeset::comp(
235 #left_layout,
236 #right_layout,
237 true,
238 false
239 )
240 }
241 }
242 Ast::Binary(BinaryOp::FixedUnpadded, left, right) => {
243 let left_layout = _reify_layout(*left);
244 let right_layout = _reify_layout(*right);
245 quote! {
246 typeset::comp(
247 #left_layout,
248 #right_layout,
249 false,
250 true
251 )
252 }
253 }
254 Ast::Binary(BinaryOp::FixedPadded, left, right) => {
255 let left_layout = _reify_layout(*left);
256 let right_layout = _reify_layout(*right);
257 quote! {
258 typeset::comp(
259 #left_layout,
260 #right_layout,
261 true,
262 true
263 )
264 }
265 }
266 Ast::Binary(BinaryOp::Newline, left, right) => {
267 let left_layout = _reify_layout(*left);
268 let right_layout = _reify_layout(*right);
269 quote! {
270 typeset::line(
271 #left_layout,
272 #right_layout
273 )
274 }
275 }
276 Ast::Binary(BinaryOp::DoubleNewline, left, right) => {
277 let left_layout = _reify_layout(*left);
278 let right_layout = _reify_layout(*right);
279 quote! {
280 typeset::line(
281 #left_layout,
282 typeset::line(
283 typeset::null(),
284 #right_layout
285 )
286 )
287 }
288 }
289 }
290}
291
292#[proc_macro]
293pub fn layout(input: TokenStream) -> TokenStream {
294 let ast = parse_macro_input!(input as Box<Ast>);
295 let output = _reify_layout(*ast);
296 quote! { #output }.into()
297}