dialectic_compiler/
parse.rs

1//! The parser for the surface syntax of the `Session!` macro.
2
3use {
4    proc_macro2::Span,
5    syn::{
6        braced,
7        parse::{Error, Parse, ParseStream, Result},
8        spanned::Spanned as SpannedExt,
9        token, Lifetime, LitInt, Token, Type,
10    },
11};
12
13use crate::{
14    syntax::{Invocation, Syntax},
15    Spanned,
16};
17
18mod kw {
19    use super::*;
20
21    syn::custom_keyword!(recv);
22    syn::custom_keyword!(send);
23    syn::custom_keyword!(call);
24    syn::custom_keyword!(choose);
25    syn::custom_keyword!(offer);
26    syn::custom_keyword!(split);
27
28    /// Return `true` if the next token is one of our keywords.
29    pub fn peek_any(input: ParseStream) -> bool {
30        input.peek(recv)
31            || input.peek(send)
32            || input.peek(call)
33            || input.peek(choose)
34            || input.peek(offer)
35            || input.peek(split)
36    }
37}
38
39impl Parse for Invocation {
40    fn parse(input: ParseStream) -> Result<Self> {
41        let mut nodes = Vec::new();
42        while !input.is_empty() {
43            let terminator_required = requires_terminator(&input);
44            nodes.push(input.parse::<Spanned<Syntax>>()?);
45
46            if !input.is_empty() && (terminator_required || input.peek(Token![;])) {
47                input.parse::<Token![;]>()?;
48            }
49        }
50
51        let ast = Spanned {
52            inner: Syntax::Block(nodes),
53            span: Span::call_site(),
54        };
55        Ok(Invocation { syntax: ast })
56    }
57}
58
59/// The direction of a split arm, either `->` or `<-`.
60#[derive(Clone, Copy, PartialEq)]
61enum Direction {
62    /// The transmit direction `->`.
63    Transmit,
64    /// The receive direction `<-`.
65    Receive,
66}
67
68/// A split arm, consisting of a [`Direction`] and a ([`Spanned`]) [`Syntax`] for the body of the
69/// arm: `<- ...` or `-> ...`. This parser also parses a terminator, if necessary or present.
70struct SplitArm {
71    /// Which direction the split arm operates in.
72    dir: Direction,
73    /// The body of the arm.
74    body: Spanned<Syntax>,
75}
76
77impl Parse for SplitArm {
78    fn parse(input: ParseStream) -> Result<Self> {
79        let lookahead = input.lookahead1();
80        let dir = if lookahead.peek(Token![->]) {
81            let _ = input.parse::<Token![->]>()?;
82            Direction::Transmit
83        } else if lookahead.peek(Token![<-]) {
84            let _ = input.parse::<Token![<-]>()?;
85            Direction::Receive
86        } else {
87            return Err(lookahead.error());
88        };
89        let terminator_required = requires_terminator(&input);
90        let arm = input.parse::<Spanned<Syntax>>()?;
91        if !input.is_empty() && (terminator_required || input.peek(Token![,])) {
92            input.parse::<Token![,]>()?;
93        }
94        Ok(SplitArm { dir, body: arm })
95    }
96}
97
98/// An arm of a choice (either in `offer` or `choose`), consisting of an index and the code it
99/// refers to: `N => ...`. This parser also parses a terminator, if necessary or if it is simply
100/// present.
101struct ChoiceArm {
102    /// The index of the arm, i.e. `0`, `1`, ... `127`.
103    index: Spanned<usize>,
104    /// The body of the arm.
105    body: Spanned<Syntax>,
106}
107
108impl Parse for ChoiceArm {
109    fn parse(input: ParseStream) -> Result<Self> {
110        let index_lit = input.parse::<LitInt>()?;
111        let index_span = index_lit.span();
112        let index = index_lit
113            .base10_parse::<usize>()
114            .map_err(|e| input.error(e))?;
115        let _ = input.parse::<Token![=>]>()?;
116        let terminator_required = requires_terminator(&input);
117        let arm = input.parse::<Spanned<Syntax>>()?;
118        if !input.is_empty() && (terminator_required || input.peek(Token![,])) {
119            input.parse::<Token![,]>()?;
120        }
121        Ok(ChoiceArm {
122            index: Spanned {
123                inner: index,
124                span: index_span,
125            },
126            body: arm,
127        })
128    }
129}
130
131/// A block of statements: `{ ... }`.
132struct Block(Spanned<Syntax>);
133
134/// Returns `true` if the next token in the `ParseStream` starts an expression that would require a
135/// terminator afterwards.
136fn requires_terminator(input: ParseStream) -> bool {
137    // It's easier to check if a terminator isn't required because checking to see whether a
138    // terminator *is* required means checking to see if we're looking at a directly injected type,
139    // which means checking to see whether it fits Rust type syntax. This is highly
140    // complex/downright ridiculous to do using just syn's peek functionality.
141    let terminator_not_required = input.peek(token::Brace)
142        || (input.peek(kw::call) && input.peek2(token::Brace))
143        || input.peek(kw::split)
144        || input.peek(kw::choose)
145        || input.peek(kw::offer)
146        || input.peek(Token![loop])
147        || input.peek(Lifetime);
148    !terminator_not_required
149}
150
151impl Parse for Block {
152    fn parse(input: ParseStream) -> Result<Self> {
153        // Ast::Block: { <Ast>; <Ast>; ... }
154        let block_span = input.span();
155        let content;
156        braced!(content in input);
157        let mut nodes = Vec::new();
158        while !content.is_empty() {
159            let terminator_required = requires_terminator(&content);
160            nodes.push(content.parse::<Spanned<Syntax>>()?);
161
162            if !content.is_empty() && (terminator_required || content.peek(Token![;])) {
163                content.parse::<Token![;]>()?;
164            }
165        }
166
167        Ok(Block(Spanned {
168            inner: Syntax::Block(nodes),
169            span: block_span,
170        }))
171    }
172}
173
174impl Parse for Spanned<Syntax> {
175    fn parse(input: ParseStream) -> Result<Self> {
176        // Take a parsed, spanned piece of syntax, join its span with the mutably referenced span,
177        // and then return the unmodified thing; helper for joining spans of multiple bits of syntax
178        // together while parsing without lots of if-lets.
179        fn with_span<T: SpannedExt>(span: &mut Span, thing: T) -> T {
180            *span = span.join(thing.span()).unwrap_or(*span);
181            thing
182        }
183
184        let lookahead = input.lookahead1();
185        if lookahead.peek(kw::recv) {
186            // Ast::Recv: recv <type>
187            let recv_span = input.parse::<kw::recv>()?.span();
188            Ok(Spanned {
189                inner: Syntax::Recv(input.parse::<Type>()?),
190                span: recv_span,
191            })
192        } else if lookahead.peek(kw::send) {
193            // Ast::Send: send <type>
194            let send_span = input.parse::<kw::send>()?.span();
195            Ok(Spanned {
196                inner: Syntax::Send(input.parse::<Type>()?),
197                span: send_span,
198            })
199        } else if lookahead.peek(kw::call) {
200            // Ast::Call: call <type> or call <block>
201            let call_span = input.parse::<kw::call>()?.span();
202            let lookahead = input.lookahead1();
203
204            let callee = if lookahead.peek(token::Brace) {
205                input.parse::<Block>()?.0
206            } else if kw::peek_any(input) {
207                return Err(input.error("expected a block or a type, but found a keyword"));
208            } else {
209                let ty = input.parse::<Type>().map_err(|mut e| {
210                    e.combine(lookahead.error());
211                    e
212                })?;
213
214                let span = ty.span();
215
216                Spanned {
217                    inner: Syntax::Type(ty),
218                    span,
219                }
220            };
221
222            Ok(Spanned {
223                inner: Syntax::Call(Box::new(callee)),
224                span: call_span,
225            })
226        } else if lookahead.peek(kw::choose) {
227            // Ast::Choose: choose { 0 => <Ast>, 1 => <Ast>, ... }
228            let kw_span = input.parse::<kw::choose>()?.span();
229            let choose_span = kw_span.join(input.span()).unwrap_or(kw_span);
230
231            let content;
232            braced!(content in input);
233            let mut choice_arms = Vec::new();
234            while !content.is_empty() {
235                choice_arms.push(content.parse::<ChoiceArm>()?);
236            }
237
238            let mut arm_asts = Vec::new();
239            for (i, choice_arm) in choice_arms.into_iter().enumerate() {
240                if i != choice_arm.index.inner as usize {
241                    return Err(Error::new(
242                        choice_arm.index.span,
243                        format!(
244                            "expected index {} in `choose` construct, found {}",
245                            i, choice_arm.index.inner
246                        ),
247                    ));
248                }
249
250                arm_asts.push(choice_arm.body);
251            }
252
253            Ok(Spanned {
254                inner: Syntax::Choose(arm_asts),
255                span: choose_span,
256            })
257        } else if lookahead.peek(kw::offer) {
258            // Ast::Offer: offer { 0 => <Ast>, 1 => <Ast>, ... }
259            let kw_span = input.parse::<kw::offer>()?.span();
260            let offer_span = kw_span.join(input.span()).unwrap_or(kw_span);
261
262            let content;
263            braced!(content in input);
264            let mut choice_arms = Vec::new();
265            while !content.is_empty() {
266                choice_arms.push(content.parse::<ChoiceArm>()?);
267            }
268
269            let mut arm_asts = Vec::new();
270            for (i, choice_arm) in choice_arms.into_iter().enumerate() {
271                if i != choice_arm.index.inner as usize {
272                    return Err(Error::new(
273                        choice_arm.index.span,
274                        format!(
275                            "expected index {} in `offer` construct, found {}",
276                            i, choice_arm.index.inner
277                        ),
278                    ));
279                }
280
281                arm_asts.push(choice_arm.body);
282            }
283
284            Ok(Spanned {
285                inner: Syntax::Offer(arm_asts),
286                span: offer_span,
287            })
288        } else if lookahead.peek(kw::split) {
289            // Ast::Split: split { <- ..., -> ... } or split { -> ..., <- ... }
290            let kw_span = input.parse::<kw::split>()?.span();
291            let split_span = kw_span.join(input.span()).unwrap_or(kw_span);
292
293            let content;
294            braced!(content in input);
295            let mut split_arms = Vec::new();
296            while !content.is_empty() {
297                split_arms.push(content.parse::<SplitArm>()?);
298            }
299
300            if split_arms.len() != 2 || split_arms[0].dir == split_arms[1].dir {
301                return Err(input.error(
302                    "split constructs must have exactly two arms, one Transmit and one inbound",
303                ));
304            }
305
306            if split_arms[0].dir == Direction::Receive {
307                split_arms.swap(0, 1);
308            }
309
310            Ok(Spanned {
311                inner: Syntax::Split {
312                    tx_only: Box::new(split_arms[0].body.clone()),
313                    rx_only: Box::new(split_arms[1].body.clone()),
314                },
315                span: split_span,
316            })
317        } else if lookahead.peek(Token![loop]) || lookahead.peek(Lifetime) {
318            // Ast::Loop: 'label: loop { ... }
319            let mut loop_span;
320            let label = if input.peek(Lifetime) {
321                loop_span = input.span();
322                let lifetime = with_span(&mut loop_span, input.parse::<Lifetime>()?);
323                let name = lifetime.ident.to_string();
324                let _ = with_span(&mut loop_span, input.parse::<Token![:]>()?);
325                let _ = with_span(&mut loop_span, input.parse::<Token![loop]>()?);
326                Some(name)
327            } else {
328                loop_span = input.parse::<Token![loop]>()?.span();
329                None
330            };
331
332            let Block(block) = input.parse::<Block>()?;
333            Ok(Spanned {
334                inner: Syntax::Loop(label, Box::new(block)),
335                span: loop_span,
336            })
337        } else if lookahead.peek(Token![break]) {
338            // Ast::Break: break 'label
339            let mut break_span = input.parse::<Token![break]>()?.span();
340            let label = if input.peek(Lifetime) {
341                let lifetime = input.parse::<Lifetime>()?;
342                let _ = with_span(&mut break_span, &lifetime);
343                Some(lifetime.ident.to_string())
344            } else {
345                None
346            };
347
348            Ok(Spanned {
349                inner: Syntax::Break(label),
350                span: break_span,
351            })
352        } else if lookahead.peek(Token![continue]) {
353            // Ast::Continue: continue 'label
354            let mut continue_span = input.parse::<Token![continue]>()?.span();
355            let label = if input.peek(Lifetime) {
356                let lifetime = input.parse::<Lifetime>()?;
357                let _ = with_span(&mut continue_span, &lifetime);
358                Some(lifetime.ident.to_string())
359            } else {
360                None
361            };
362
363            Ok(Spanned {
364                inner: Syntax::Continue(label),
365                span: continue_span,
366            })
367        } else if lookahead.peek(token::Brace) {
368            // Ast::Block: { <Ast>; <Ast>; ... }
369            Ok(input.parse::<Block>()?.0)
370        } else {
371            // Attempt to parse as a direct type Ast::Type: <type> Otherwise, fail and report all
372            // other errors from the lookahead.
373            let ty = match input.parse::<Type>() {
374                Ok(ty) => ty,
375                Err(e) => {
376                    let mut combined = lookahead.error();
377                    combined.combine(input.error("parsing as a type failed"));
378                    combined.combine(e);
379                    return Err(combined);
380                }
381            };
382
383            let span = ty.span();
384
385            Ok(Spanned {
386                inner: Syntax::Type(ty),
387                span,
388            })
389        }
390    }
391}