Skip to content

Commit

Permalink
hugr(feat/sexpr): Input and output s-expressions via streams.
Browse files Browse the repository at this point in the history
  • Loading branch information
zrho committed Jun 20, 2024
1 parent 88f7f6f commit 3ca215a
Show file tree
Hide file tree
Showing 13 changed files with 713 additions and 518 deletions.
18 changes: 9 additions & 9 deletions devenv.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
"devenv": {
"locked": {
"dir": "src/modules",
"lastModified": 1718265154,
"lastModified": 1718793085,
"owner": "cachix",
"repo": "devenv",
"rev": "1983f635c29dc68bb0d29b3a7e227579a1d98788",
"treeHash": "65018ab26133a442cd98ab058d07be455165a203",
"rev": "3bdcfeb72948c40f8cb5f09660a0751a1c1acc6e",
"treeHash": "3cdc9f0487f54349b850d429060f85f17a9a0971",
"type": "github"
},
"original": {
Expand Down Expand Up @@ -109,11 +109,11 @@
},
"nixpkgs-stable_2": {
"locked": {
"lastModified": 1718060059,
"lastModified": 1718447546,
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a3c8d64ba846725f040582b2d3b875466d2115bd",
"treeHash": "4378720d991b2af92b2e078b80bff5ec363a11c5",
"rev": "842253bf992c3a7157b67600c2857193f126563a",
"treeHash": "f83c8a39ca9b08ef20784df1ba861d15f54c26dd",
"type": "github"
},
"original": {
Expand Down Expand Up @@ -158,11 +158,11 @@
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1718185781,
"lastModified": 1718691984,
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "fa486e694e1fe7774f20350bb15ff933f670dc3c",
"treeHash": "d5c8aa18755af93f07200192dfba2a16379cb666",
"rev": "021ae0101c5fc387fc7e99e98181c44bb34170ed",
"treeHash": "cd70022e2b9bec27289d8f9d53b45a5b69d89d81",
"type": "github"
},
"original": {
Expand Down
83 changes: 46 additions & 37 deletions hugr-sexpr-derive/src/import.rs → hugr-sexpr-derive/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ use syn::{spanned::Spanned, DataStruct, DeriveInput};

use crate::common::{get_first_type_arg, parse_sexpr_attributes, FieldKind};

pub fn derive_import_impl(derive_input: DeriveInput) -> syn::Result<TokenStream> {
pub fn derive_input_impl(derive_input: DeriveInput) -> syn::Result<TokenStream> {
match &derive_input.data {
syn::Data::Struct(data_struct) => derive_import_struct(&derive_input, data_struct),
syn::Data::Struct(data_struct) => derive_input_struct(&derive_input, data_struct),
syn::Data::Enum(_) => Err(syn::Error::new(
derive_input.span(),
"Can not derive Import for enums.",
"Can not derive Input for enums.",
)),
syn::Data::Union(_) => Err(syn::Error::new(
derive_input.span(),
"Can not derive Import for unions.",
"Can not derive Input for unions.",
)),
}
}

fn derive_import_struct(
fn derive_input_struct(
derive_input: &DeriveInput,
data_struct: &DataStruct,
) -> syn::Result<TokenStream> {
Expand Down Expand Up @@ -50,7 +50,7 @@ fn derive_import_struct(
let Some(field_ident) = &field.ident else {
return Err(syn::Error::new_spanned(
field,
"Fields must be named to derive Import.",
"Fields must be named to derive Input.",
));
};

Expand All @@ -76,11 +76,11 @@ fn derive_import_struct(
}

code_positional.push(quote! {
let (values, #field_ident_var) = <_ as ::hugr_sexpr::import::Import<'a, A>>::import(values)?;
let #field_ident_var = <_ as ::hugr_sexpr::input::Input<I>>::parse(stream)?;
});

code_where.push(quote! {
#field_type: ::hugr_sexpr::import::Import<'a, A>,
#field_type: ::hugr_sexpr::input::Input<I>,
});
}
FieldKind::NamedRequired => {
Expand All @@ -94,8 +94,9 @@ fn derive_import_struct(

code_field_required.push(quote! {
let Some(#field_ident_var) = #field_ident_var else {
return Err(::hugr_sexpr::import::ImportError::new(
#missing_field_message
return Err(::hugr_sexpr::input::ParseError::new(
#missing_field_message,
stream.parent_span()
));
};
});
Expand All @@ -105,19 +106,19 @@ fn derive_import_struct(
code_named_match.push(quote! {
#field_name => {
if #field_ident_var.is_some() {
return Err(::hugr_sexpr::import::ImportError::new_with_meta(
return Err(::hugr_sexpr::input::ParseError::new(
#duplicate_field_message,
field_value.meta().clone()
inner_stream.parent_span()
));
}

let (values, value) = <_ as ::hugr_sexpr::import::Import<'a, A>>::import(values)?;
let value = <_ as ::hugr_sexpr::input::Input<I>>::parse(&mut inner_stream)?;
#field_ident_var = Some(value);
},
});

code_where.push(quote! {
#field_type: ::hugr_sexpr::import::Import<'a, A>,
#field_type: ::hugr_sexpr::input::Input<I>,
});
}
FieldKind::NamedOptional => {
Expand All @@ -132,19 +133,19 @@ fn derive_import_struct(
code_named_match.push(quote! {
#field_name => {
if #field_ident_var.is_some() {
return Err(::hugr_sexpr::import::ImportError::new_with_meta(
return Err(::hugr_sexpr::input::ParseError::new(
#duplicate_field_message,
field_value.meta().clone()
inner_stream.parent_span()
));
}

let (values, value) = <_ as ::hugr_sexpr::import::Import<'a, A>>::import(values)?;
let value = <_ as ::hugr_sexpr::input::Input<I>>::parse(&mut inner_stream)?;
#field_ident_var = Some(value);
}
});

// As with the positional and required fields, we need to ensure that
// the type of the field is parseable by adding a constraint bound for `Import`.
// the type of the field is parseable by adding a constraint bound for `Input`.
// But since the type of an optional field is wrapped in an `Option`, we
// first need to extract it.
let inner_type = get_first_type_arg(field_type).ok_or(syn::Error::new_spanned(
Expand All @@ -153,7 +154,7 @@ fn derive_import_struct(
))?;

code_where.push(quote! {
#inner_type: ::hugr_sexpr::import::Import<'a, A>,
#inner_type: ::hugr_sexpr::input::Input<I>,
});
}
FieldKind::NamedRepeated => {
Expand All @@ -165,7 +166,7 @@ fn derive_import_struct(

code_named_match.push(quote! {
#field_name => {
let (values, value) = <_ as ::hugr_sexpr::import::Import<'a, A>>::import(values)?;
let value = <_ as ::hugr_sexpr::input::Input<I>>::parse(&mut inner_stream)?;
#field_ident_var.push(value);
}
});
Expand All @@ -179,7 +180,7 @@ fn derive_import_struct(
))?;

code_where.push(quote! {
#inner_type: ::hugr_sexpr::import::Import<'a, A>,
#inner_type: ::hugr_sexpr::input::Input<I>,
});
}
};
Expand All @@ -191,19 +192,27 @@ fn derive_import_struct(

let code_named_match: TokenStream = code_named_match.into_iter().collect();
let code_named = quote! {
for field_value in values {
let (head, values) = field_value.as_list_with_head().ok_or_else(||
::hugr_sexpr::import::ImportError::new_with_meta(
"Expected field.",
field_value.meta().clone()
)
)?;
while let Some(token_tree) = stream.next() {
let ::hugr_sexpr::input::TokenTree::List(mut inner_stream) = token_tree else {
return Err(::hugr_sexpr::input::ParseError::new(
"expected field",
stream.span()
));
};

let Some(::hugr_sexpr::input::TokenTree::Symbol(head)) = inner_stream.next() else {
return Err(::hugr_sexpr::input::ParseError::new(
"expected field name",
inner_stream.parent_span()
));
};

match head.as_ref() {
#code_named_match
_ => {
return Err(::hugr_sexpr::import::ImportError::new_with_meta(
"Unknown field.",
field_value.meta().clone()
unknown_name => {
return Err(::hugr_sexpr::input::ParseError::new(
format!("unknown field `{}`", unknown_name),
inner_stream.parent_span()
));
}
};
Expand All @@ -218,18 +227,18 @@ fn derive_import_struct(

Ok(quote! {
#[automatically_derived]
impl<'a, A, #struct_generics> ::hugr_sexpr::import::Import<'a, A> for #struct_ident<#struct_generics>
where A: Clone, #code_where {
fn import(values: &'a [::hugr_sexpr::Value<A>]) -> ::hugr_sexpr::import::ImportResult<'a, Self, A>
impl<I, #struct_generics> ::hugr_sexpr::input::Input<I> for #struct_ident<#struct_generics>
where I: ::hugr_sexpr::input::InputStream, #code_where {
fn parse(stream: &mut I) -> ::std::result::Result<Self, ::hugr_sexpr::input::ParseError<I::Span>>
where
Self: Sized {
#code_positional
#code_field_setup
#code_named
#code_field_required
Ok((&[], Self {
Ok(Self {
#constr_fields
}))
})
}
}
})
Expand Down
18 changes: 9 additions & 9 deletions hugr-sexpr-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@
use syn::{parse_macro_input, DeriveInput};

pub(crate) mod common;
mod export;
mod import;
mod input;
mod output;

/// Derive the [`Import`] trait.
#[proc_macro_derive(Import, attributes(sexpr))]
pub fn derive_import(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
/// Derive the [`Input`] trait.
#[proc_macro_derive(Input, attributes(sexpr))]
pub fn derive_input(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let derive_input = parse_macro_input!(input as DeriveInput);
import::derive_import_impl(derive_input)
input::derive_input_impl(derive_input)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}

/// Derive the [`Export`] trait.
#[proc_macro_derive(Export, attributes(sexpr))]
/// Derive the [`Output`] trait.
#[proc_macro_derive(Output, attributes(sexpr))]
pub fn derive_export(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let derive_input = parse_macro_input!(input as DeriveInput);
export::derive_export_impl(derive_input)
output::derive_output_impl(derive_input)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
Loading

0 comments on commit 3ca215a

Please sign in to comment.