typeset_parser/
lib.rs

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}