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