use {
proc_macro2::Span,
syn::{
braced,
parse::{Error, Parse, ParseStream, Result},
spanned::Spanned as SpannedExt,
token, Lifetime, LitInt, Token, Type,
},
};
use crate::{
syntax::{Invocation, Syntax},
Spanned,
};
mod kw {
use super::*;
syn::custom_keyword!(recv);
syn::custom_keyword!(send);
syn::custom_keyword!(call);
syn::custom_keyword!(choose);
syn::custom_keyword!(offer);
syn::custom_keyword!(split);
pub fn peek_any(input: ParseStream) -> bool {
input.peek(recv)
|| input.peek(send)
|| input.peek(call)
|| input.peek(choose)
|| input.peek(offer)
|| input.peek(split)
}
}
impl Parse for Invocation {
fn parse(input: ParseStream) -> Result<Self> {
let mut nodes = Vec::new();
while !input.is_empty() {
let terminator_required = requires_terminator(&input);
nodes.push(input.parse::<Spanned<Syntax>>()?);
if !input.is_empty() && (terminator_required || input.peek(Token![;])) {
input.parse::<Token![;]>()?;
}
}
let ast = Spanned {
inner: Syntax::Block(nodes),
span: Span::call_site(),
};
Ok(Invocation { syntax: ast })
}
}
#[derive(Clone, Copy, PartialEq)]
enum Direction {
Transmit,
Receive,
}
struct SplitArm {
dir: Direction,
body: Spanned<Syntax>,
}
impl Parse for SplitArm {
fn parse(input: ParseStream) -> Result<Self> {
let lookahead = input.lookahead1();
let dir = if lookahead.peek(Token![->]) {
let _ = input.parse::<Token![->]>()?;
Direction::Transmit
} else if lookahead.peek(Token![<-]) {
let _ = input.parse::<Token![<-]>()?;
Direction::Receive
} else {
return Err(lookahead.error());
};
let terminator_required = requires_terminator(&input);
let arm = input.parse::<Spanned<Syntax>>()?;
if !input.is_empty() && (terminator_required || input.peek(Token![,])) {
input.parse::<Token![,]>()?;
}
Ok(SplitArm { dir, body: arm })
}
}
struct ChoiceArm {
index: Spanned<usize>,
body: Spanned<Syntax>,
}
impl Parse for ChoiceArm {
fn parse(input: ParseStream) -> Result<Self> {
let index_lit = input.parse::<LitInt>()?;
let index_span = index_lit.span();
let index = index_lit
.base10_parse::<usize>()
.map_err(|e| input.error(e))?;
let _ = input.parse::<Token![=>]>()?;
let terminator_required = requires_terminator(&input);
let arm = input.parse::<Spanned<Syntax>>()?;
if !input.is_empty() && (terminator_required || input.peek(Token![,])) {
input.parse::<Token![,]>()?;
}
Ok(ChoiceArm {
index: Spanned {
inner: index,
span: index_span,
},
body: arm,
})
}
}
struct Block(Spanned<Syntax>);
fn requires_terminator(input: ParseStream) -> bool {
let terminator_not_required = input.peek(token::Brace)
|| (input.peek(kw::call) && input.peek2(token::Brace))
|| input.peek(kw::split)
|| input.peek(kw::choose)
|| input.peek(kw::offer)
|| input.peek(Token![loop])
|| input.peek(Lifetime);
!terminator_not_required
}
impl Parse for Block {
fn parse(input: ParseStream) -> Result<Self> {
let block_span = input.span();
let content;
braced!(content in input);
let mut nodes = Vec::new();
while !content.is_empty() {
let terminator_required = requires_terminator(&content);
nodes.push(content.parse::<Spanned<Syntax>>()?);
if !content.is_empty() && (terminator_required || content.peek(Token![;])) {
content.parse::<Token![;]>()?;
}
}
Ok(Block(Spanned {
inner: Syntax::Block(nodes),
span: block_span,
}))
}
}
impl Parse for Spanned<Syntax> {
fn parse(input: ParseStream) -> Result<Self> {
fn with_span<T: SpannedExt>(span: &mut Span, thing: T) -> T {
*span = span.join(thing.span()).unwrap_or(*span);
thing
}
let lookahead = input.lookahead1();
if lookahead.peek(kw::recv) {
let recv_span = input.parse::<kw::recv>()?.span();
Ok(Spanned {
inner: Syntax::Recv(input.parse::<Type>()?),
span: recv_span,
})
} else if lookahead.peek(kw::send) {
let send_span = input.parse::<kw::send>()?.span();
Ok(Spanned {
inner: Syntax::Send(input.parse::<Type>()?),
span: send_span,
})
} else if lookahead.peek(kw::call) {
let call_span = input.parse::<kw::call>()?.span();
let lookahead = input.lookahead1();
let callee = if lookahead.peek(token::Brace) {
input.parse::<Block>()?.0
} else if kw::peek_any(input) {
return Err(input.error("expected a block or a type, but found a keyword"));
} else {
let ty = input.parse::<Type>().map_err(|mut e| {
e.combine(lookahead.error());
e
})?;
let span = ty.span();
Spanned {
inner: Syntax::Type(ty),
span,
}
};
Ok(Spanned {
inner: Syntax::Call(Box::new(callee)),
span: call_span,
})
} else if lookahead.peek(kw::choose) {
let kw_span = input.parse::<kw::choose>()?.span();
let choose_span = kw_span.join(input.span()).unwrap_or(kw_span);
let content;
braced!(content in input);
let mut choice_arms = Vec::new();
while !content.is_empty() {
choice_arms.push(content.parse::<ChoiceArm>()?);
}
let mut arm_asts = Vec::new();
for (i, choice_arm) in choice_arms.into_iter().enumerate() {
if i != choice_arm.index.inner as usize {
return Err(Error::new(
choice_arm.index.span,
format!(
"expected index {} in `choose` construct, found {}",
i, choice_arm.index.inner
),
));
}
arm_asts.push(choice_arm.body);
}
Ok(Spanned {
inner: Syntax::Choose(arm_asts),
span: choose_span,
})
} else if lookahead.peek(kw::offer) {
let kw_span = input.parse::<kw::offer>()?.span();
let offer_span = kw_span.join(input.span()).unwrap_or(kw_span);
let content;
braced!(content in input);
let mut choice_arms = Vec::new();
while !content.is_empty() {
choice_arms.push(content.parse::<ChoiceArm>()?);
}
let mut arm_asts = Vec::new();
for (i, choice_arm) in choice_arms.into_iter().enumerate() {
if i != choice_arm.index.inner as usize {
return Err(Error::new(
choice_arm.index.span,
format!(
"expected index {} in `offer` construct, found {}",
i, choice_arm.index.inner
),
));
}
arm_asts.push(choice_arm.body);
}
Ok(Spanned {
inner: Syntax::Offer(arm_asts),
span: offer_span,
})
} else if lookahead.peek(kw::split) {
let kw_span = input.parse::<kw::split>()?.span();
let split_span = kw_span.join(input.span()).unwrap_or(kw_span);
let content;
braced!(content in input);
let mut split_arms = Vec::new();
while !content.is_empty() {
split_arms.push(content.parse::<SplitArm>()?);
}
if split_arms.len() != 2 || split_arms[0].dir == split_arms[1].dir {
return Err(input.error(
"split constructs must have exactly two arms, one Transmit and one inbound",
));
}
if split_arms[0].dir == Direction::Receive {
split_arms.swap(0, 1);
}
Ok(Spanned {
inner: Syntax::Split {
tx_only: Box::new(split_arms[0].body.clone()),
rx_only: Box::new(split_arms[1].body.clone()),
},
span: split_span,
})
} else if lookahead.peek(Token![loop]) || lookahead.peek(Lifetime) {
let mut loop_span;
let label = if input.peek(Lifetime) {
loop_span = input.span();
let lifetime = with_span(&mut loop_span, input.parse::<Lifetime>()?);
let name = lifetime.ident.to_string();
let _ = with_span(&mut loop_span, input.parse::<Token![:]>()?);
let _ = with_span(&mut loop_span, input.parse::<Token![loop]>()?);
Some(name)
} else {
loop_span = input.parse::<Token![loop]>()?.span();
None
};
let Block(block) = input.parse::<Block>()?;
Ok(Spanned {
inner: Syntax::Loop(label, Box::new(block)),
span: loop_span,
})
} else if lookahead.peek(Token![break]) {
let mut break_span = input.parse::<Token![break]>()?.span();
let label = if input.peek(Lifetime) {
let lifetime = input.parse::<Lifetime>()?;
let _ = with_span(&mut break_span, &lifetime);
Some(lifetime.ident.to_string())
} else {
None
};
Ok(Spanned {
inner: Syntax::Break(label),
span: break_span,
})
} else if lookahead.peek(Token![continue]) {
let mut continue_span = input.parse::<Token![continue]>()?.span();
let label = if input.peek(Lifetime) {
let lifetime = input.parse::<Lifetime>()?;
let _ = with_span(&mut continue_span, &lifetime);
Some(lifetime.ident.to_string())
} else {
None
};
Ok(Spanned {
inner: Syntax::Continue(label),
span: continue_span,
})
} else if lookahead.peek(token::Brace) {
Ok(input.parse::<Block>()?.0)
} else {
let ty = match input.parse::<Type>() {
Ok(ty) => ty,
Err(e) => {
let mut combined = lookahead.error();
combined.combine(input.error("parsing as a type failed"));
combined.combine(e);
return Err(combined);
}
};
let span = ty.span();
Ok(Spanned {
inner: Syntax::Type(ty),
span,
})
}
}
}