Skip to content

Commit

Permalink
use syn::Error instead of panicking (#46)
Browse files Browse the repository at this point in the history
Co-authored-by: Shadaj Laddad <[email protected]>
  • Loading branch information
soqb and shadaj authored Oct 3, 2023
1 parent e78d1ee commit fa2b07c
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 50 deletions.
62 changes: 62 additions & 0 deletions macro/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::marker::PhantomData;

/// An iterator that maps [`Result`]s to their [`Ok`] values
/// and stores combined errors within itself.
struct CollectingShunt<'a, I, A> {
iter: I,
err: &'a mut Option<syn::Error>,
_marker: PhantomData<fn() -> A>,
}

impl<'a, I, A> Iterator for CollectingShunt<'a, I, A>
where
I: Iterator<Item = syn::Result<A>>,
{
type Item = A;

fn next(&mut self) -> Option<Self::Item> {
match self.iter.next() {
Some(Ok(x)) => Some(x),
Some(Err(another)) => {
match self.err {
Some(x) => x.combine(another),
ref mut x => **x = Some(another),
}
None
}
_ => None,
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
let (_, upper) = self.iter.size_hint();
(0, upper)
}
}

pub trait IteratorExt<A>: Iterator<Item = syn::Result<A>> {
/// Reduces an iterator with items of type [`syn::Result<T>`] into one large collection,
/// [combining] errors and [collecting] successes.
///
/// [combining]: syn::Error::combine
/// [collecting]: FromIterator
fn sift<T>(self) -> syn::Result<T>
where
Self: Sized,
T: FromIterator<A>,
{
let mut err = None;
let iter = CollectingShunt {
iter: self,
err: &mut err,
_marker: PhantomData,
};
let collection = iter.collect();
match err {
Some(error) => Err(error),
None => Ok(collection),
}
}
}

impl<A, T> IteratorExt<A> for T where T: Iterator<Item = syn::Result<A>> {}
73 changes: 47 additions & 26 deletions macro/src/expansion.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
use std::collections::HashSet;

use crate::errors::IteratorExt as _;
use proc_macro2::Span;
use quote::{quote, ToTokens};
use rust_sitter_common::*;
use syn::{parse::Parse, punctuated::Punctuated, *};

fn is_sitter_attr(attr: &Attribute) -> bool {
let ident = &attr.path.segments.iter().next().unwrap().ident;
ident == "rust_sitter"
attr.path
.segments
.iter()
.next()
.map(|segment| segment.ident == "rust_sitter")
.unwrap_or(false)
}

enum ParamOrField {
Expand Down Expand Up @@ -117,7 +122,7 @@ fn gen_struct_or_variant(
containing_type: Ident,
container_attrs: Vec<Attribute>,
out: &mut Vec<Item>,
) {
) -> Result<()> {
if fields == Fields::Unit {
let dummy_field = Field {
attrs: container_attrs,
Expand Down Expand Up @@ -163,7 +168,7 @@ fn gen_struct_or_variant(
.iter()
.find(|attr| attr.path == syn::parse_quote!(rust_sitter::skip))
{
skip_attrs.parse_args::<syn::Expr>().unwrap()
skip_attrs.parse_args::<syn::Expr>()?
} else {
let ident_str = field
.ident
Expand All @@ -178,20 +183,20 @@ fn gen_struct_or_variant(
}
};

if field.ident.is_none() {
ParamOrField::Param(expr)
} else {
let field_name = field.ident.as_ref().unwrap();
let field = if let Some(field_name) = &field.ident {
have_named_field = true;
ParamOrField::Field(FieldValue {
attrs: vec![],
member: Member::Named(field_name.clone()),
colon_token: Some(Token![:](Span::call_site())),
expr,
})
}
} else {
ParamOrField::Param(expr)
};
Ok(field)
})
.collect::<Vec<ParamOrField>>();
.sift::<Vec<ParamOrField>>()?;

let construct_name = match variant_ident {
Some(ident) => quote! {
Expand Down Expand Up @@ -240,9 +245,11 @@ fn gen_struct_or_variant(
#construct_expr
}
});

Ok(())
}

pub fn expand_grammar(input: ItemMod) -> ItemMod {
pub fn expand_grammar(input: ItemMod) -> Result<ItemMod> {
let grammar_name = input
.attrs
.iter()
Expand All @@ -254,17 +261,26 @@ pub fn expand_grammar(input: ItemMod) -> ItemMod {
lit: Lit::Str(s),
})) = grammar_name_expr
{
Some(s.value())
Some(Ok(s.value()))
} else {
panic!("Expected string literal for grammar name");
Some(Err(syn::Error::new(
Span::call_site(),
"Expected a string literal grammar name",
)))
}
} else {
None
}
})
.expect("Each grammar must have a name");
.transpose()?
.ok_or_else(|| syn::Error::new(Span::call_site(), "Each grammar must have a name"))?;

let (brace, new_contents) = input.content.unwrap();
let (brace, new_contents) = input.content.ok_or_else(|| {
syn::Error::new(
Span::call_site(),
"Expected the module to have inline contents (`mod my_module { .. }` syntax)",
)
})?;

let root_type = new_contents
.iter()
Expand All @@ -282,15 +298,20 @@ pub fn expand_grammar(input: ItemMod) -> ItemMod {
}
_ => None,
})
.expect("Each parser must have the root type annotated with `#[rust_sitter::language]`");
.ok_or_else(|| {
syn::Error::new(
Span::call_site(),
"Each parser must have the root type annotated with `#[rust_sitter::language]`",
)
})?;

let mut transformed: Vec<Item> = new_contents
.iter()
.cloned()
.flat_map(|c| match c {
.map(|c| match c {
Item::Enum(mut e) => {
let mut impl_body = vec![];
e.variants.iter().for_each(|v| {
e.variants.iter().map(|v| {
gen_struct_or_variant(
format!("{}_{}", e.ident, v.ident),
v.fields.clone(),
Expand All @@ -299,7 +320,7 @@ pub fn expand_grammar(input: ItemMod) -> ItemMod {
v.attrs.clone(),
&mut impl_body,
)
});
}).sift::<()>()?;

let match_cases: Vec<Arm> = e
.variants
Expand Down Expand Up @@ -347,7 +368,7 @@ pub fn expand_grammar(input: ItemMod) -> ItemMod {
}
};

vec![Item::Enum(e), extract_impl]
Ok(vec![Item::Enum(e), extract_impl])
}

Item::Struct(mut s) => {
Expand All @@ -360,7 +381,7 @@ pub fn expand_grammar(input: ItemMod) -> ItemMod {
s.ident.clone(),
s.attrs.clone(),
&mut impl_body,
);
)?;

s.attrs.retain(|a| !is_sitter_attr(a));
s.fields.iter_mut().for_each(|f| {
Expand All @@ -384,12 +405,12 @@ pub fn expand_grammar(input: ItemMod) -> ItemMod {
}
};

vec![Item::Struct(s), extract_impl]
Ok(vec![Item::Struct(s), extract_impl])
}

o => vec![o],
o => Ok(vec![o]),
})
.collect();
.sift::<Vec<_>>()?.into_iter().flatten().collect();

let tree_sitter_ident = Ident::new(&format!("tree_sitter_{grammar_name}"), Span::call_site());

Expand Down Expand Up @@ -430,12 +451,12 @@ pub fn expand_grammar(input: ItemMod) -> ItemMod {

let mut filtered_attrs = input.attrs;
filtered_attrs.retain(|a| !is_sitter_attr(a));
ItemMod {
Ok(ItemMod {
attrs: filtered_attrs,
vis: input.vis,
mod_token: input.mod_token,
ident: input.ident,
content: Some((brace, transformed)),
semi: input.semi,
}
})
}
Loading

0 comments on commit fa2b07c

Please sign in to comment.