From f88ef45ee6e285df59c7aa5cec935de331b4b6e0 Mon Sep 17 00:00:00 2001 From: Pg Biel <9021226+PgBiel@users.noreply.github.com> Date: Wed, 3 May 2023 09:20:53 -0300 Subject: Function scopes (#1032) --- macros/src/element.rs | 28 +++++++++------------------- macros/src/func.rs | 9 +++++++-- macros/src/util.rs | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 21 deletions(-) (limited to 'macros/src') diff --git a/macros/src/element.rs b/macros/src/element.rs index 37ca19eb..403af103 100644 --- a/macros/src/element.rs +++ b/macros/src/element.rs @@ -15,6 +15,7 @@ struct Elem { ident: Ident, capable: Vec, fields: Vec, + scope: Option, } struct Field { @@ -28,7 +29,7 @@ struct Field { synthesized: bool, fold: bool, resolve: bool, - parse: Option, + parse: Option, default: syn::Expr, vis: syn::Visibility, ident: Ident, @@ -50,21 +51,6 @@ impl Field { } } -struct FieldParser { - prefix: Vec, - expr: syn::Stmt, -} - -impl Parse for FieldParser { - fn parse(input: ParseStream) -> Result { - let mut stmts = syn::Block::parse_within(input)?; - let Some(expr) = stmts.pop() else { - return Err(input.error("expected at least on expression")); - }; - Ok(Self { prefix: stmts, expr }) - } -} - /// Preprocess the element's definition. fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result { let syn::Fields::Named(named) = &body.fields else { @@ -137,7 +123,8 @@ fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result { .into_iter() .collect(); - let docs = documentation(&body.attrs); + let mut attrs = body.attrs.clone(); + let docs = documentation(&attrs); let mut lines = docs.split('\n').collect(); let category = meta_line(&mut lines, "Category")?.into(); let display = meta_line(&mut lines, "Display")?.into(); @@ -152,9 +139,10 @@ fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result { ident: body.ident.clone(), capable, fields, + scope: parse_attr(&mut attrs, "scope")?.flatten(), }; - validate_attrs(&body.attrs)?; + validate_attrs(&attrs)?; Ok(element) } @@ -351,6 +339,7 @@ fn create_pack_impl(element: &Elem) -> TokenStream { .iter() .filter(|field| !field.internal && !field.synthesized) .map(create_param_info); + let scope = create_scope_builder(element.scope.as_ref()); quote! { impl ::typst::model::Element for #ident { fn pack(self) -> ::typst::model::Content { @@ -377,6 +366,7 @@ fn create_pack_impl(element: &Elem) -> TokenStream { params: ::std::vec![#(#infos),*], returns: ::std::vec!["content"], category: #category, + scope: #scope, }), }; (&NATIVE).into() @@ -519,7 +509,7 @@ fn create_set_impl(element: &Elem) -> TokenStream { /// Create argument parsing code for a field. fn create_field_parser(field: &Field) -> (TokenStream, TokenStream) { - if let Some(FieldParser { prefix, expr }) = &field.parse { + if let Some(BlockWithReturn { prefix, expr }) = &field.parse { return (quote! { #(#prefix);* }, quote! { #expr }); } diff --git a/macros/src/func.rs b/macros/src/func.rs index 386ed7c4..f3de6822 100644 --- a/macros/src/func.rs +++ b/macros/src/func.rs @@ -18,6 +18,7 @@ struct Func { params: Vec, returns: Vec, body: syn::Block, + scope: Option, } struct Param { @@ -72,7 +73,8 @@ fn prepare(item: &syn::ItemFn) -> Result { validate_attrs(&attrs)?; } - let docs = documentation(&item.attrs); + let mut attrs = item.attrs.clone(); + let docs = documentation(&attrs); let mut lines = docs.split('\n').collect(); let returns = meta_line(&mut lines, "Returns")? .split(" or ") @@ -92,9 +94,10 @@ fn prepare(item: &syn::ItemFn) -> Result { params, returns, body: (*item.block).clone(), + scope: parse_attr(&mut attrs, "scope")?.flatten(), }; - validate_attrs(&item.attrs)?; + validate_attrs(&attrs)?; Ok(func) } @@ -113,6 +116,7 @@ fn create(func: &Func) -> TokenStream { } = func; let handlers = params.iter().filter(|param| !param.external).map(create_param_parser); let params = params.iter().map(create_param_info); + let scope = create_scope_builder(func.scope.as_ref()); quote! { #[doc = #docs] #vis fn #ident() -> &'static ::typst::eval::NativeFunc { @@ -129,6 +133,7 @@ fn create(func: &Func) -> TokenStream { params: ::std::vec![#(#params),*], returns: ::std::vec![#(#returns),*], category: #category, + scope: #scope, }), }; &FUNC diff --git a/macros/src/util.rs b/macros/src/util.rs index 53a8354e..6b683e5d 100644 --- a/macros/src/util.rs +++ b/macros/src/util.rs @@ -18,6 +18,27 @@ macro_rules! bail { }; } +/// For parsing attributes of the form: +/// #[attr( +/// statement; +/// statement; +/// returned_expression +/// )] +pub struct BlockWithReturn { + pub prefix: Vec, + pub expr: syn::Stmt, +} + +impl Parse for BlockWithReturn { + fn parse(input: ParseStream) -> Result { + let mut stmts = syn::Block::parse_within(input)?; + let Some(expr) = stmts.pop() else { + return Err(input.error("expected at least one expression")); + }; + Ok(Self { prefix: stmts, expr }) + } +} + /// Whether an attribute list has a specified attribute. pub fn has_attr(attrs: &mut Vec, target: &str) -> bool { take_attr(attrs, target).is_some() @@ -88,3 +109,16 @@ pub fn meta_line<'a>(lines: &mut Vec<&'a str>, key: &str) -> Result<&'a str> { None => bail!(callsite, "missing metadata key: {}", key), } } + +/// Creates a block responsible for building a Scope. +pub fn create_scope_builder(scope_block: Option<&BlockWithReturn>) -> TokenStream { + if let Some(BlockWithReturn { prefix, expr }) = scope_block { + quote! { { + let mut scope = ::typst::eval::Scope::deduplicating(); + #(#prefix);* + #expr + } } + } else { + quote! { ::typst::eval::Scope::new() } + } +} -- cgit v1.2.3