dialectic_compiler/
target.rs

1//! The target language of the `Session!` macro, produced by the compiler.
2
3use {
4    proc_macro2::TokenStream,
5    quote::{quote_spanned, ToTokens},
6    std::{fmt, rc::Rc},
7    syn::{Path, Type},
8};
9
10use crate::Spanned;
11
12/// The target language of the macro: the type level language of session types in Dialectic.
13///
14/// This is a one-to-one mapping to the literal syntax you would write without using the `Session!`
15/// macro. The only constructors which don't correspond directly to constructs with `Session`
16/// implementations are [`Target::Then`], which translates to a type-level function invocation to
17/// concatenate two session types, and [`Target::Type`], which translates to an embedding of some
18/// arbitrary session type by name (i.e. defined elsewhere as a synonym).
19#[derive(Clone, Debug)]
20pub enum Target {
21    /// Session type: `Done`.
22    Done,
23    /// Session type: `Recv<T, P>`.
24    Recv(Type, Rc<Spanned<Target>>),
25    /// Session type: `Send<T, P>`.
26    Send(Type, Rc<Spanned<Target>>),
27    /// Session type: `Choose<(P, ...)>`.
28    Choose(Vec<Spanned<Target>>),
29    /// Session type: `Offer<(P, ...)>`.
30    Offer(Vec<Spanned<Target>>),
31    /// Session type: `Loop<...>`.
32    Loop(Rc<Spanned<Target>>),
33    /// Session type: `Continue<N>`.
34    Continue(usize),
35    /// Session type: `Split<P, Q, R>`.
36    Split {
37        /// The transmit-only half.
38        tx_only: Rc<Spanned<Target>>,
39        /// The receive-only half.
40        rx_only: Rc<Spanned<Target>>,
41        /// The continuation.
42        cont: Rc<Spanned<Target>>,
43    },
44    /// Session type: `Call<P, Q>`.
45    Call(Rc<Spanned<Target>>, Rc<Spanned<Target>>),
46    /// Session type: `<P as Then<Q>>::Combined`.
47    Then(Rc<Spanned<Target>>, Rc<Spanned<Target>>),
48    /// Some arbitrary session type referenced by name.
49    Type(Type),
50}
51
52impl fmt::Display for Target {
53    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54        use Target::*;
55        match self {
56            Done => write!(f, "Done")?,
57            Recv(t, s) => write!(f, "Recv<{}, {}>", t.to_token_stream(), s)?,
58            Send(t, s) => write!(f, "Send<{}, {}>", t.to_token_stream(), s)?,
59            Loop(s) => write!(f, "Loop<{}>", s)?,
60            Split {
61                tx_only: s,
62                rx_only: p,
63                cont: q,
64            } => write!(f, "Split<{}, {}, {}>", s, p, q)?,
65            Call(s, p) => write!(f, "Call<{}, {}>", s, p)?,
66            Then(s, p) => write!(f, "<{} as Then<{}>>::Combined", s, p)?,
67            Choose(cs) => {
68                let count = cs.len();
69                write!(f, "Choose<(")?;
70                for (i, c) in cs.iter().enumerate() {
71                    write!(f, "{}", c)?;
72                    if i + 1 < count {
73                        write!(f, ", ")?;
74                    }
75                }
76                if count == 1 {
77                    write!(f, ",")?;
78                }
79                write!(f, ")>")?;
80            }
81            Offer(cs) => {
82                let count = cs.len();
83                write!(f, "Offer<(")?;
84                for (i, c) in cs.iter().enumerate() {
85                    write!(f, "{}", c)?;
86                    if i + 1 < count {
87                        write!(f, ", ")?;
88                    }
89                }
90                if count == 1 {
91                    write!(f, ",")?;
92                }
93                write!(f, ")>")?;
94            }
95            Continue(n) => {
96                write!(f, "Continue<{}>", n)?;
97            }
98            Type(s) => write!(f, "{}", s.to_token_stream())?,
99        }
100        Ok(())
101    }
102}
103
104impl Spanned<Target> {
105    /// Convert this `Spanned<Target>` into a `TokenStream` using a provided crate name when
106    /// referencing types from dialectic.
107    pub fn to_token_stream_with_crate_name(&self, dialectic_crate: &Path) -> TokenStream {
108        let mut tokens = TokenStream::new();
109        self.to_tokens_with_crate_name(dialectic_crate, &mut tokens);
110        tokens
111    }
112
113    /// Convert this `Spanned<Target>` into tokens and append them to the provided `TokenStream`
114    /// using a provided crate name when referencing types from dialectic.
115    pub fn to_tokens_with_crate_name(&self, dialectic_crate: &Path, tokens: &mut TokenStream) {
116        use Target::*;
117
118        // We assign the associated span of the target to any type tokens generated, in an attempt
119        // to get at least some type errors/issues to show up in the right place.
120        let span = self.span;
121
122        match &self.inner {
123            Done => quote_spanned! {span=> #dialectic_crate::types::Done }.to_tokens(tokens),
124            Recv(t, s) => {
125                let s = s.to_token_stream_with_crate_name(dialectic_crate);
126                quote_spanned!(span=> #dialectic_crate::types::Recv<#t, #s>).to_tokens(tokens);
127            }
128            Send(t, s) => {
129                let s = s.to_token_stream_with_crate_name(dialectic_crate);
130                quote_spanned!(span=> #dialectic_crate::types::Send<#t, #s>).to_tokens(tokens);
131            }
132            Loop(s) => {
133                let s = s.to_token_stream_with_crate_name(dialectic_crate);
134                quote_spanned!(span=> #dialectic_crate::types::Loop<#s>).to_tokens(tokens);
135            }
136            Split {
137                tx_only: s,
138                rx_only: p,
139                cont: q,
140            } => {
141                let s = s.to_token_stream_with_crate_name(dialectic_crate);
142                let p = p.to_token_stream_with_crate_name(dialectic_crate);
143                let q = q.to_token_stream_with_crate_name(dialectic_crate);
144                quote_spanned!(span=> #dialectic_crate::types::Split<#s, #p, #q>).to_tokens(tokens);
145            }
146            Call(s, p) => {
147                let s = s.to_token_stream_with_crate_name(dialectic_crate);
148                let p = p.to_token_stream_with_crate_name(dialectic_crate);
149                quote_spanned!(span=> #dialectic_crate::types::Call<#s, #p>).to_tokens(tokens);
150            }
151            Then(s, p) => {
152                let s = s.to_token_stream_with_crate_name(dialectic_crate);
153                let p = p.to_token_stream_with_crate_name(dialectic_crate);
154                quote_spanned!(span=> <#s as #dialectic_crate::types::Then<#p>>::Combined)
155                    .to_tokens(tokens);
156            }
157            Choose(cs) => {
158                let cs = cs
159                    .iter()
160                    .map(|c| c.to_token_stream_with_crate_name(dialectic_crate));
161                quote_spanned!(span=> #dialectic_crate::types::Choose<(#(#cs,)*)>).to_tokens(tokens)
162            }
163            Offer(cs) => {
164                let cs = cs
165                    .iter()
166                    .map(|c| c.to_token_stream_with_crate_name(dialectic_crate));
167                quote_spanned!(span=> #dialectic_crate::types::Offer<(#(#cs,)*)>).to_tokens(tokens)
168            }
169            Continue(n) => {
170                quote_spanned!(span=> #dialectic_crate::types::Continue<#n>).to_tokens(tokens)
171            }
172            Type(s) => quote_spanned!(span=> #s).to_tokens(tokens),
173        }
174    }
175}
176
177impl ToTokens for Spanned<Target> {
178    fn to_tokens(&self, tokens: &mut TokenStream) {
179        self.to_tokens_with_crate_name(&crate::dialectic_path(), tokens);
180    }
181}