summaryrefslogtreecommitdiff
path: root/macros
diff options
context:
space:
mode:
authorPg Biel <9021226+PgBiel@users.noreply.github.com>2023-05-03 09:20:53 -0300
committerGitHub <noreply@github.com>2023-05-03 14:20:53 +0200
commitf88ef45ee6e285df59c7aa5cec935de331b4b6e0 (patch)
treeeab5481d4b50d1d57adb4d122d7fa023dee2dcec /macros
parentdb6a710638cf26ddcd09b8fba74b9d1caf6cb4b8 (diff)
Function scopes (#1032)
Diffstat (limited to 'macros')
-rw-r--r--macros/src/element.rs28
-rw-r--r--macros/src/func.rs9
-rw-r--r--macros/src/util.rs34
3 files changed, 50 insertions, 21 deletions
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<Ident>,
fields: Vec<Field>,
+ scope: Option<BlockWithReturn>,
}
struct Field {
@@ -28,7 +29,7 @@ struct Field {
synthesized: bool,
fold: bool,
resolve: bool,
- parse: Option<FieldParser>,
+ parse: Option<BlockWithReturn>,
default: syn::Expr,
vis: syn::Visibility,
ident: Ident,
@@ -50,21 +51,6 @@ impl Field {
}
}
-struct FieldParser {
- prefix: Vec<syn::Stmt>,
- expr: syn::Stmt,
-}
-
-impl Parse for FieldParser {
- fn parse(input: ParseStream) -> Result<Self> {
- 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<Elem> {
let syn::Fields::Named(named) = &body.fields else {
@@ -137,7 +123,8 @@ fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result<Elem> {
.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<Elem> {
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<Param>,
returns: Vec<String>,
body: syn::Block,
+ scope: Option<BlockWithReturn>,
}
struct Param {
@@ -72,7 +73,8 @@ fn prepare(item: &syn::ItemFn) -> Result<Func> {
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<Func> {
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<syn::Stmt>,
+ pub expr: syn::Stmt,
+}
+
+impl Parse for BlockWithReturn {
+ fn parse(input: ParseStream) -> Result<Self> {
+ 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<syn::Attribute>, 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() }
+ }
+}