diff options
Diffstat (limited to 'syn-mid/src/pat.rs')
-rw-r--r-- | syn-mid/src/pat.rs | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/syn-mid/src/pat.rs b/syn-mid/src/pat.rs new file mode 100644 index 0000000..8f95381 --- /dev/null +++ b/syn-mid/src/pat.rs @@ -0,0 +1,413 @@ +use syn::{punctuated::Punctuated, token, Attribute, Ident, Member, Path, Token, Type}; + +ast_enum_of_structs! { + /// A pattern in a local binding, function signature, match expression, or + /// various other places. + /// + /// # Syntax tree enum + /// + /// This type is a [syntax tree enum]. + /// + /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums + pub enum Pat { + /// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`. + Ident(PatIdent), + + /// A path pattern like `Color::Red`. + Path(PatPath), + + /// A reference pattern: `&mut var`. + Reference(PatReference), + + /// A struct or struct variant pattern: `Variant { x, y, .. }`. + Struct(PatStruct), + + /// A tuple pattern: `(a, b)`. + Tuple(PatTuple), + + /// A tuple struct or tuple variant pattern: `Variant(x, y, .., z)`. + TupleStruct(PatTupleStruct), + + /// A type ascription pattern: `foo: f64`. + Type(PatType), + + /// A pattern that matches any value: `_`. + Wild(PatWild), + + #[doc(hidden)] + __Nonexhaustive, + } +} + +ast_struct! { + /// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`. + pub struct PatIdent { + pub attrs: Vec<Attribute>, + pub by_ref: Option<Token![ref]>, + pub mutability: Option<Token![mut]>, + pub ident: Ident, + } +} + +ast_struct! { + /// A path pattern like `Color::Red`. + pub struct PatPath { + pub attrs: Vec<Attribute>, + pub path: Path, + } +} + +ast_struct! { + /// A reference pattern: `&mut var`. + pub struct PatReference { + pub attrs: Vec<Attribute>, + pub and_token: Token![&], + pub mutability: Option<Token![mut]>, + pub pat: Box<Pat>, + } +} + +ast_struct! { + /// A struct or struct variant pattern: `Variant { x, y, .. }`. + pub struct PatStruct { + pub attrs: Vec<Attribute>, + pub path: Path, + pub brace_token: token::Brace, + pub fields: Punctuated<FieldPat, Token![,]>, + pub dot2_token: Option<Token![..]>, + } +} + +ast_struct! { + /// A tuple pattern: `(a, b)`. + pub struct PatTuple { + pub attrs: Vec<Attribute>, + pub paren_token: token::Paren, + pub elems: Punctuated<Pat, Token![,]>, + } +} + +ast_struct! { + /// A tuple struct or tuple variant pattern: `Variant(x, y, .., z)`. + pub struct PatTupleStruct { + pub attrs: Vec<Attribute>, + pub path: Path, + pub pat: PatTuple, + } +} + +ast_struct! { + /// A type ascription pattern: `foo: f64`. + pub struct PatType { + pub attrs: Vec<Attribute>, + pub pat: Box<Pat>, + pub colon_token: Token![:], + pub ty: Box<Type>, + } +} + +ast_struct! { + /// A pattern that matches any value: `_`. + pub struct PatWild { + pub attrs: Vec<Attribute>, + pub underscore_token: Token![_], + } +} + +ast_struct! { + /// A single field in a struct pattern. + /// + /// Patterns like the fields of Foo `{ x, ref y, ref mut z }` are treated + /// the same as `x: x, y: ref y, z: ref mut z` but there is no colon token. + pub struct FieldPat { + pub attrs: Vec<Attribute>, + pub member: Member, + pub colon_token: Option<Token![:]>, + pub pat: Box<Pat>, + } +} + +mod parsing { + use syn::{ + braced, + ext::IdentExt, + parenthesized, + parse::{Parse, ParseStream, Result}, + punctuated::Punctuated, + token, Ident, Member, Path, Token, + }; + + use crate::path; + + use super::{ + FieldPat, Pat, PatIdent, PatPath, PatReference, PatStruct, PatTuple, PatTupleStruct, + PatWild, + }; + + impl Parse for Pat { + fn parse(input: ParseStream<'_>) -> Result<Self> { + let lookahead = input.lookahead1(); + if lookahead.peek(Ident) + && ({ + input.peek2(Token![::]) + || input.peek2(token::Brace) + || input.peek2(token::Paren) + }) + || input.peek(Token![self]) && input.peek2(Token![::]) + || lookahead.peek(Token![::]) + || lookahead.peek(Token![<]) + || input.peek(Token![Self]) + || input.peek(Token![super]) + || input.peek(Token![extern]) + || input.peek(Token![crate]) + { + pat_path_or_struct(input) + } else if lookahead.peek(Token![_]) { + input.call(pat_wild).map(Pat::Wild) + } else if lookahead.peek(Token![ref]) + || lookahead.peek(Token![mut]) + || input.peek(Token![self]) + || input.peek(Ident) + { + input.call(pat_ident).map(Pat::Ident) + } else if lookahead.peek(Token![&]) { + input.call(pat_reference).map(Pat::Reference) + } else if lookahead.peek(token::Paren) { + input.call(pat_tuple).map(Pat::Tuple) + } else { + Err(lookahead.error()) + } + } + } + + fn pat_path_or_struct(input: ParseStream<'_>) -> Result<Pat> { + let path = path::parse_path(input)?; + + if input.peek(token::Brace) { + pat_struct(input, path).map(Pat::Struct) + } else if input.peek(token::Paren) { + pat_tuple_struct(input, path).map(Pat::TupleStruct) + } else { + Ok(Pat::Path(PatPath { + attrs: Vec::new(), + path, + })) + } + } + + fn pat_wild(input: ParseStream<'_>) -> Result<PatWild> { + Ok(PatWild { + attrs: Vec::new(), + underscore_token: input.parse()?, + }) + } + + fn pat_ident(input: ParseStream<'_>) -> Result<PatIdent> { + Ok(PatIdent { + attrs: Vec::new(), + by_ref: input.parse()?, + mutability: input.parse()?, + ident: input.call(Ident::parse_any)?, + }) + } + + fn pat_tuple_struct(input: ParseStream<'_>, path: Path) -> Result<PatTupleStruct> { + Ok(PatTupleStruct { + attrs: Vec::new(), + path, + pat: input.call(pat_tuple)?, + }) + } + + fn pat_struct(input: ParseStream<'_>, path: Path) -> Result<PatStruct> { + let content; + let brace_token = braced!(content in input); + + let mut fields = Punctuated::new(); + while !content.is_empty() && !content.peek(Token![..]) { + let value = content.call(field_pat)?; + fields.push_value(value); + if !content.peek(Token![,]) { + break; + } + let punct: Token![,] = content.parse()?; + fields.push_punct(punct); + } + + let dot2_token = if fields.empty_or_trailing() && content.peek(Token![..]) { + Some(content.parse()?) + } else { + None + }; + + Ok(PatStruct { + attrs: Vec::new(), + path, + brace_token, + fields, + dot2_token, + }) + } + + fn field_pat(input: ParseStream<'_>) -> Result<FieldPat> { + let boxed: Option<Token![box]> = input.parse()?; + let by_ref: Option<Token![ref]> = input.parse()?; + let mutability: Option<Token![mut]> = input.parse()?; + let member: Member = input.parse()?; + + if boxed.is_none() && by_ref.is_none() && mutability.is_none() && input.peek(Token![:]) + || is_unnamed(&member) + { + return Ok(FieldPat { + attrs: Vec::new(), + member, + colon_token: input.parse()?, + pat: input.parse()?, + }); + } + + let ident = match member { + Member::Named(ident) => ident, + Member::Unnamed(_) => unreachable!(), + }; + + let pat = Pat::Ident(PatIdent { + attrs: Vec::new(), + by_ref, + mutability, + ident: ident.clone(), + }); + + Ok(FieldPat { + member: Member::Named(ident), + pat: Box::new(pat), + attrs: Vec::new(), + colon_token: None, + }) + } + + fn pat_tuple(input: ParseStream<'_>) -> Result<PatTuple> { + let content; + let paren_token = parenthesized!(content in input); + + let mut elems = Punctuated::new(); + while !content.is_empty() { + let value: Pat = content.parse()?; + elems.push_value(value); + if content.is_empty() { + break; + } + let punct = content.parse()?; + elems.push_punct(punct); + } + + Ok(PatTuple { + attrs: Vec::new(), + paren_token, + elems, + }) + } + + fn pat_reference(input: ParseStream<'_>) -> Result<PatReference> { + Ok(PatReference { + attrs: Vec::new(), + and_token: input.parse()?, + mutability: input.parse()?, + pat: input.parse()?, + }) + } + + fn is_unnamed(member: &Member) -> bool { + match member { + Member::Named(_) => false, + Member::Unnamed(_) => true, + } + } +} + +mod printing { + use proc_macro2::TokenStream; + use quote::{ToTokens, TokenStreamExt}; + use syn::Token; + + use super::{ + FieldPat, PatIdent, PatPath, PatReference, PatStruct, PatTuple, PatTupleStruct, PatType, + PatWild, + }; + + impl ToTokens for PatWild { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.underscore_token.to_tokens(tokens); + } + } + + impl ToTokens for PatIdent { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.by_ref.to_tokens(tokens); + self.mutability.to_tokens(tokens); + self.ident.to_tokens(tokens); + } + } + + impl ToTokens for PatStruct { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.path.to_tokens(tokens); + self.brace_token.surround(tokens, |tokens| { + self.fields.to_tokens(tokens); + // NOTE: We need a comma before the dot2 token if it is present. + if !self.fields.empty_or_trailing() && self.dot2_token.is_some() { + <Token![,]>::default().to_tokens(tokens); + } + self.dot2_token.to_tokens(tokens); + }); + } + } + + impl ToTokens for PatTupleStruct { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.path.to_tokens(tokens); + self.pat.to_tokens(tokens); + } + } + + impl ToTokens for PatType { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append_all(&self.attrs); + self.pat.to_tokens(tokens); + self.colon_token.to_tokens(tokens); + self.ty.to_tokens(tokens); + } + } + + impl ToTokens for PatPath { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.path.to_tokens(tokens) + } + } + + impl ToTokens for PatTuple { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.paren_token.surround(tokens, |tokens| { + self.elems.to_tokens(tokens); + }); + } + } + + impl ToTokens for PatReference { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.and_token.to_tokens(tokens); + self.mutability.to_tokens(tokens); + self.pat.to_tokens(tokens); + } + } + + impl ToTokens for FieldPat { + fn to_tokens(&self, tokens: &mut TokenStream) { + if let Some(colon_token) = &self.colon_token { + self.member.to_tokens(tokens); + colon_token.to_tokens(tokens); + } + self.pat.to_tokens(tokens); + } + } +} |