Skip to content

Commit

Permalink
Add more magic to Parse impl
Browse files Browse the repository at this point in the history
  • Loading branch information
udoprog committed Oct 6, 2020
1 parent 1164734 commit eb93090
Show file tree
Hide file tree
Showing 23 changed files with 193 additions and 331 deletions.
65 changes: 56 additions & 9 deletions crates/rune-macros/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use quote::quote_spanned;
use quote::{quote, ToTokens};
use syn::spanned::Spanned as _;
use syn::Meta::*;
use syn::MetaNameValue;
use syn::NestedMeta::*;

/// Parsed `#[rune(..)]` field attributes.
Expand All @@ -19,8 +20,8 @@ pub(crate) struct FieldAttrs {
skip: Option<Span>,
/// `#[rune(optional)]`
pub(crate) optional: Option<Span>,
/// `#[rune(attributes)]`
pub(crate) attributes: Option<Span>,
/// `#[rune(meta)]`
pub(crate) meta: Option<Span>,
/// A single field marked with `#[rune(span)]`.
pub(crate) span: Option<Span>,
}
Expand All @@ -32,9 +33,27 @@ impl FieldAttrs {
}
}

/// The parsing implementations to build.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ParseKind {
/// Generate default functions.
Default,
/// Only generate meta parse function.
MetaOnly,
}

impl Default for ParseKind {
fn default() -> Self {
Self::Default
}
}

/// Parsed ast derive attributes.
#[derive(Default)]
pub(crate) struct DeriveAttrs {}
pub(crate) struct DeriveAttrs {
/// The parse kind to build.
pub(crate) parse: ParseKind,
}

pub(crate) struct Context {
pub(crate) errors: Vec<syn::Error>,
Expand Down Expand Up @@ -115,17 +134,45 @@ impl Context {
}

/// Parse field attributes.
pub(crate) fn pase_derive_attributes(
pub(crate) fn parse_derive_attributes(
&mut self,
input: &[syn::Attribute],
) -> Option<DeriveAttrs> {
let attrs = DeriveAttrs::default();
let mut attrs = DeriveAttrs::default();

for attr in input {
#[allow(clippy::never_loop)] // I guess this is on purpose?
for meta in self.get_meta_items(attr, RUNE)? {
self.errors
.push(syn::Error::new_spanned(meta, "unsupported attribute"));
match &meta {
// Parse `#[rune(id)]`
Meta(NameValue(MetaNameValue {
path,
lit: syn::Lit::Str(s),
..
})) if *path == PARSE => {
let parse = match s.value().as_str() {
"meta_only" => ParseKind::MetaOnly,
other => {
self.errors.push(syn::Error::new_spanned(
meta,
format!(
"unsupported `#[rune(parse = ..)]` argument `{}`",
other
),
));
return None;
}
};

attrs.parse = parse;
}
meta => {
self.errors
.push(syn::Error::new_spanned(meta, "unsupported attribute"));

return None;
}
}
}
}

Expand Down Expand Up @@ -160,8 +207,8 @@ impl Context {
attrs.optional = Some(meta.span());
}
// Parse `#[rune(attributes)]`
Meta(Path(word)) if *word == ATTRIBUTES => {
attrs.attributes = Some(meta.span());
Meta(Path(word)) if *word == META => {
attrs.meta = Some(meta.span());
}
// Parse `#[rune(span)]`
Meta(Path(word)) if *word == SPAN => {
Expand Down
3 changes: 2 additions & 1 deletion crates/rune-macros/src/internals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ pub const ID: Symbol = Symbol("id");
pub const SKIP: Symbol = Symbol("skip");
pub const ITER: Symbol = Symbol("iter");
pub const OPTIONAL: Symbol = Symbol("optional");
pub const ATTRIBUTES: Symbol = Symbol("attributes");
pub const META: Symbol = Symbol("meta");
pub const SPAN: Symbol = Symbol("span");
pub const PARSE: Symbol = Symbol("parse");

impl PartialEq<Symbol> for syn::Ident {
fn eq(&self, word: &Symbol) -> bool {
Expand Down
2 changes: 1 addition & 1 deletion crates/rune-macros/src/option_spanned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl Expander {

/// Expand on a struct.
fn expand_enum(&mut self, input: &syn::DeriveInput, st: &syn::DataEnum) -> Option<TokenStream> {
let _ = self.ctx.pase_derive_attributes(&input.attrs)?;
let _ = self.ctx.parse_derive_attributes(&input.attrs)?;

let mut impl_spanned = Vec::new();

Expand Down
73 changes: 46 additions & 27 deletions crates/rune-macros/src/parse.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::context::Context;
use crate::context::{Context, ParseKind};
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use syn::spanned::Spanned as _;
Expand Down Expand Up @@ -96,35 +96,44 @@ impl Expander {
named: &syn::FieldsNamed,
) -> Option<TokenStream> {
let ident = &input.ident;
let mut attrs_field: Option<(usize, &syn::Ident)> = None;
let mut fields = Vec::new();

let mut meta_args = Vec::new();
let mut meta_parse = Vec::new();
let mut meta_fields = Vec::new();

let derive_attrs = self.ctx.parse_derive_attributes(&input.attrs)?;
let mut skipped = 0;

for (i, field) in named.named.iter().enumerate() {
let attrs = self.ctx.parse_field_attributes(&field.attrs)?;
let ident = self.ctx.field_ident(&field)?;

if attrs.id.is_some() {
fields.push(quote_spanned! { field.span() => #ident: Default::default() });
skipped += 1;
continue;
}

if attrs.attributes.is_some() {
if let Some((idx, ident)) = &attrs_field {
if attrs.meta.is_some() {
if i - skipped != meta_fields.len() {
self.ctx.errors.push(syn::Error::new_spanned(
field,
format!(
"only one field may have `#[rune({})]`, \
but field is second occurrence within this struct. The first \
occurrence was at field #{} `{}`.",
crate::internals::ATTRIBUTES,
idx,
quote! { #ident }
"The first sequence of fields may have `#[rune({})]`, \
but field is outside of that sequence.",
crate::internals::META,
),
));
return None;
} else {
let ident = self.ctx.field_ident(field)?;
attrs_field = Some((i + 1, ident));
let ty = &field.ty;
meta_args.push(quote_spanned!(field.span() => #ident: #ty));
meta_parse
.push(quote_spanned!(field.span() => let #ident: #ty = parser.parse()?));
fields.push(quote_spanned! { field.span() => #ident });
meta_fields.push(ident);
continue;
}
}
Expand All @@ -136,30 +145,38 @@ impl Expander {
let parser = &self.ctx.parser;
let parse_error = &self.ctx.parse_error;

if let Some((_, attrs_ident)) = attrs_field {
let inner = if let ParseKind::MetaOnly = derive_attrs.parse {
None
} else {
Some(quote_spanned! {
named.span() =>
impl #parse for #ident {
fn parse(parser: &mut #parser<'_>) -> Result<Self, #parse_error> {
#(#meta_parse;)*
Self::parse_with_meta(parser, #(#meta_fields,)*)
}
}
})
};

let output = if !meta_args.is_empty() {
quote_spanned! {
named.span() =>
impl #ident {
#[doc = "Parse #ident and attach the given attributes"]
pub fn parse_with_attributes(parser: &mut #parser<'_>,
#attrs_ident: ::std::vec::Vec<crate::ast::Attribute>
) -> Result<Self, #parse_error> {
#[doc = "Parse #ident and attach the given meta"]
pub fn parse_with_meta(parser: &mut #parser<'_>, #(#meta_args,)*)
-> Result<Self, #parse_error>
{
Ok(Self {
#attrs_ident,
#(#fields,)*
})
}
}

impl #parse for #ident {
fn parse(parser: &mut #parser<'_>) -> Result<Self, #parse_error> {
let attributes: ::std::vec::Vec<crate::ast::Attribute> = parser.parse()?;
Self::parse_with_attributes(parser, attributes)
}
}
})
#inner
}
} else {
Some(quote_spanned! {
quote_spanned! {
named.span() =>
impl #parse for #ident {
fn parse(parser: &mut #parser<'_>) -> Result<Self, #parse_error> {
Expand All @@ -168,7 +185,9 @@ impl Expander {
})
}
}
})
}
}
};

Some(output)
}
}
2 changes: 1 addition & 1 deletion crates/rune-macros/src/spanned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl Expander {

/// Expand on a struct.
fn expand_enum(&mut self, input: &syn::DeriveInput, st: &syn::DataEnum) -> Option<TokenStream> {
let _ = self.ctx.pase_derive_attributes(&input.attrs)?;
let _ = self.ctx.parse_derive_attributes(&input.attrs)?;

let mut impl_spanned = Vec::new();

Expand Down
4 changes: 2 additions & 2 deletions crates/rune-macros/src/to_tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl Expander {
input: &syn::DeriveInput,
st: &syn::DataStruct,
) -> Option<TokenStream> {
let _ = self.ctx.pase_derive_attributes(&input.attrs)?;
let _ = self.ctx.parse_derive_attributes(&input.attrs)?;
let inner = self.expand_struct_fields(input, &st.fields)?;

Some(quote! {
Expand All @@ -66,7 +66,7 @@ impl Expander {

/// Expand on a struct.
fn expand_enum(&mut self, input: &syn::DeriveInput, st: &syn::DataEnum) -> Option<TokenStream> {
let _ = self.ctx.pase_derive_attributes(&input.attrs)?;
let _ = self.ctx.parse_derive_attributes(&input.attrs)?;

let mut impl_into_tokens = Vec::new();

Expand Down
35 changes: 14 additions & 21 deletions crates/rune/src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ impl Expr {
}

if ast::Lit::peek_in_expr(parser)? {
return Ok(ast::Expr::ExprLit(ast::ExprLit::parse_with_attributes(
return Ok(ast::Expr::ExprLit(ast::ExprLit::parse_with_meta(
parser,
take(attributes),
)?));
Expand Down Expand Up @@ -341,14 +341,10 @@ impl Expr {
take(attributes),
take(&mut label),
)?),
ast::Kind::Let => Self::ExprLet(ast::ExprLet::parse_with_attributes(
parser,
take(attributes),
)?),
ast::Kind::If => Self::ExprIf(ast::ExprIf::parse_with_attributes(
parser,
take(attributes),
)?),
ast::Kind::Let => {
Self::ExprLet(ast::ExprLet::parse_with_meta(parser, take(attributes))?)
}
ast::Kind::If => Self::ExprIf(ast::ExprIf::parse_with_meta(parser, take(attributes))?),
ast::Kind::Match => Self::ExprMatch(ast::ExprMatch::parse_with_attributes(
parser,
take(attributes),
Expand All @@ -361,18 +357,15 @@ impl Expr {
attributes: take(attributes),
block: parser.parse()?,
}),
ast::Kind::Break => Self::ExprBreak(ast::ExprBreak::parse_with_attributes(
parser,
take(attributes),
)?),
ast::Kind::Yield => Self::ExprYield(ast::ExprYield::parse_with_attributes(
parser,
take(attributes),
)?),
ast::Kind::Return => Self::ExprReturn(ast::ExprReturn::parse_with_attributes(
parser,
take(attributes),
)?),
ast::Kind::Break => {
Self::ExprBreak(ast::ExprBreak::parse_with_meta(parser, take(attributes))?)
}
ast::Kind::Yield => {
Self::ExprYield(ast::ExprYield::parse_with_meta(parser, take(attributes))?)
}
ast::Kind::Return => {
Self::ExprReturn(ast::ExprReturn::parse_with_meta(parser, take(attributes))?)
}
_ => {
return Err(ParseError::expected(t, "expression"));
}
Expand Down
18 changes: 3 additions & 15 deletions crates/rune/src/ast/expr_break.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ use crate::{Parse, ParseError, Parser, Peek, Spanned, ToTokens};
/// testing::roundtrip::<ast::ExprBreak>("break 42");
/// testing::roundtrip::<ast::ExprBreak>("#[attr] break 42");
/// ```
#[derive(Debug, Clone, PartialEq, Eq, ToTokens, Spanned)]
#[derive(Debug, Clone, PartialEq, Eq, Parse, ToTokens, Spanned)]
#[rune(parse = "meta_only")]
pub struct ExprBreak {
/// The attributes of the `break` expression
#[rune(iter, attributes)]
#[rune(iter, meta)]
pub attributes: Vec<ast::Attribute>,
/// The return token.
pub break_token: ast::Break,
Expand All @@ -22,19 +23,6 @@ pub struct ExprBreak {
pub expr: Option<ExprBreakValue>,
}

impl ExprBreak {
pub(crate) fn parse_with_attributes(
parser: &mut Parser<'_>,
attributes: Vec<ast::Attribute>,
) -> Result<Self, ParseError> {
Ok(Self {
attributes,
break_token: parser.parse()?,
expr: parser.parse()?,
})
}
}

expr_parse!(ExprBreak, "break expression");

/// Things that we can break on.
Expand Down
Loading

0 comments on commit eb93090

Please sign in to comment.