summaryrefslogtreecommitdiff
path: root/macros/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-07-02 19:59:52 +0200
committerLaurenz <laurmaedje@gmail.com>2023-07-02 20:07:43 +0200
commitebfdb1dafa430786db10dad2ef7d5467c1bdbed1 (patch)
tree2bbc24ddb4124c4bb14dec0e536129d4de37b056 /macros/src
parent3ab19185093d7709f824b95b979060ce125389d8 (diff)
Move everything into `crates/` directory
Diffstat (limited to 'macros/src')
-rw-r--r--macros/src/castable.rs331
-rw-r--r--macros/src/element.rs554
-rw-r--r--macros/src/func.rs268
-rw-r--r--macros/src/lib.rs63
-rw-r--r--macros/src/symbols.rs78
-rw-r--r--macros/src/util.rs145
6 files changed, 0 insertions, 1439 deletions
diff --git a/macros/src/castable.rs b/macros/src/castable.rs
deleted file mode 100644
index 05c1b4d1..00000000
--- a/macros/src/castable.rs
+++ /dev/null
@@ -1,331 +0,0 @@
-use super::*;
-
-/// Expand the `#[derive(Cast)]` macro.
-pub fn derive_cast(item: &DeriveInput) -> Result<TokenStream> {
- let ty = &item.ident;
-
- let syn::Data::Enum(data) = &item.data else {
- bail!(item, "only enums are supported");
- };
-
- let mut variants = vec![];
- for variant in &data.variants {
- if let Some((_, expr)) = &variant.discriminant {
- bail!(expr, "explicit discriminant is not allowed");
- }
-
- let string = if let Some(attr) =
- variant.attrs.iter().find(|attr| attr.path().is_ident("string"))
- {
- attr.parse_args::<syn::LitStr>()?.value()
- } else {
- kebab_case(&variant.ident)
- };
-
- variants.push(Variant {
- ident: variant.ident.clone(),
- string,
- docs: documentation(&variant.attrs),
- });
- }
-
- let strs_to_variants = variants.iter().map(|Variant { ident, string, docs }| {
- quote! {
- #[doc = #docs]
- #string => Self::#ident
- }
- });
-
- let variants_to_strs = variants.iter().map(|Variant { ident, string, .. }| {
- quote! {
- #ty::#ident => #string
- }
- });
-
- Ok(quote! {
- ::typst::eval::cast! {
- #ty,
- self => ::typst::eval::IntoValue::into_value(match self {
- #(#variants_to_strs),*
- }),
- #(#strs_to_variants),*
- }
- })
-}
-
-/// An enum variant in a `derive(Cast)`.
-struct Variant {
- ident: Ident,
- string: String,
- docs: String,
-}
-
-/// Expand the `cast!` macro.
-pub fn cast(stream: TokenStream) -> Result<TokenStream> {
- let input: CastInput = syn::parse2(stream)?;
- let ty = &input.ty;
- let eval = quote! { ::typst::eval };
-
- let castable_body = create_castable_body(&input);
- let describe_body = create_describe_body(&input);
- let into_value_body = create_into_value_body(&input);
- let from_value_body = create_from_value_body(&input);
-
- let reflect = (!input.from_value.is_empty() || input.name.is_some()).then(|| {
- quote! {
- impl #eval::Reflect for #ty {
- fn describe() -> #eval::CastInfo {
- #describe_body
- }
-
- fn castable(value: &#eval::Value) -> bool {
- #castable_body
- }
- }
- }
- });
-
- let into_value = (input.into_value.is_some() || input.name.is_some()).then(|| {
- quote! {
- impl #eval::IntoValue for #ty {
- fn into_value(self) -> #eval::Value {
- #into_value_body
- }
- }
- }
- });
-
- let from_value = (!input.from_value.is_empty() || input.name.is_some()).then(|| {
- quote! {
- impl #eval::FromValue for #ty {
- fn from_value(value: #eval::Value) -> ::typst::diag::StrResult<Self> {
- #from_value_body
- }
- }
- }
- });
-
- let ty = input.name.as_ref().map(|name| {
- quote! {
- impl #eval::Type for #ty {
- const TYPE_NAME: &'static str = #name;
- }
- }
- });
-
- Ok(quote! {
- #reflect
- #into_value
- #from_value
- #ty
- })
-}
-
-/// The input to `cast!`.
-struct CastInput {
- ty: syn::Type,
- name: Option<syn::LitStr>,
- into_value: Option<syn::Expr>,
- from_value: Punctuated<Cast, Token![,]>,
-}
-
-impl Parse for CastInput {
- fn parse(input: ParseStream) -> Result<Self> {
- let ty;
- let mut name = None;
- if input.peek(syn::Token![type]) {
- let _: syn::Token![type] = input.parse()?;
- ty = input.parse()?;
- let _: syn::Token![:] = input.parse()?;
- name = Some(input.parse()?);
- } else {
- ty = input.parse()?;
- }
-
- let _: syn::Token![,] = input.parse()?;
-
- let mut into_value = None;
- if input.peek(syn::Token![self]) {
- let _: syn::Token![self] = input.parse()?;
- let _: syn::Token![=>] = input.parse()?;
- into_value = Some(input.parse()?);
- let _: syn::Token![,] = input.parse()?;
- }
-
- let from_value = Punctuated::parse_terminated(input)?;
- Ok(Self { ty, name, into_value, from_value })
- }
-}
-
-impl Parse for Cast {
- fn parse(input: ParseStream) -> Result<Self> {
- let attrs = input.call(syn::Attribute::parse_outer)?;
- let pattern = input.parse()?;
- let _: syn::Token![=>] = input.parse()?;
- let expr = input.parse()?;
- Ok(Self { attrs, pattern, expr })
- }
-}
-
-impl Parse for Pattern {
- fn parse(input: ParseStream) -> Result<Self> {
- if input.peek(syn::LitStr) {
- Ok(Pattern::Str(input.parse()?))
- } else {
- let pat = syn::Pat::parse_single(input)?;
- let _: syn::Token![:] = input.parse()?;
- let ty = input.parse()?;
- Ok(Pattern::Ty(pat, ty))
- }
- }
-}
-
-/// A single cast, e.g. `v: i64 => Self::Int(v)`.
-struct Cast {
- attrs: Vec<syn::Attribute>,
- pattern: Pattern,
- expr: syn::Expr,
-}
-
-/// A pattern in a cast, e.g.`"ascender"` or `v: i64`.
-enum Pattern {
- Str(syn::LitStr),
- Ty(syn::Pat, syn::Type),
-}
-
-fn create_castable_body(input: &CastInput) -> TokenStream {
- let mut strings = vec![];
- let mut casts = vec![];
-
- for cast in &input.from_value {
- match &cast.pattern {
- Pattern::Str(lit) => {
- strings.push(quote! { #lit => return true });
- }
- Pattern::Ty(_, ty) => {
- casts.push(quote! {
- if <#ty as ::typst::eval::Reflect>::castable(value) {
- return true;
- }
- });
- }
- }
- }
-
- let dynamic_check = input.name.is_some().then(|| {
- quote! {
- if let ::typst::eval::Value::Dyn(dynamic) = &value {
- if dynamic.is::<Self>() {
- return true;
- }
- }
- }
- });
-
- let str_check = (!strings.is_empty()).then(|| {
- quote! {
- if let ::typst::eval::Value::Str(string) = &value {
- match string.as_str() {
- #(#strings,)*
- _ => {}
- }
- }
- }
- });
-
- quote! {
- #dynamic_check
- #str_check
- #(#casts)*
- false
- }
-}
-
-fn create_describe_body(input: &CastInput) -> TokenStream {
- let mut infos = vec![];
-
- for cast in &input.from_value {
- let docs = documentation(&cast.attrs);
- infos.push(match &cast.pattern {
- Pattern::Str(lit) => {
- quote! {
- ::typst::eval::CastInfo::Value(
- ::typst::eval::IntoValue::into_value(#lit),
- #docs,
- )
- }
- }
- Pattern::Ty(_, ty) => {
- quote! { <#ty as ::typst::eval::Reflect>::describe() }
- }
- });
- }
-
- if let Some(name) = &input.name {
- infos.push(quote! {
- ::typst::eval::CastInfo::Type(#name)
- });
- }
-
- quote! {
- #(#infos)+*
- }
-}
-
-fn create_into_value_body(input: &CastInput) -> TokenStream {
- if let Some(expr) = &input.into_value {
- quote! { #expr }
- } else {
- quote! { ::typst::eval::Value::dynamic(self) }
- }
-}
-
-fn create_from_value_body(input: &CastInput) -> TokenStream {
- let mut string_arms = vec![];
- let mut cast_checks = vec![];
-
- for cast in &input.from_value {
- let expr = &cast.expr;
- match &cast.pattern {
- Pattern::Str(lit) => {
- string_arms.push(quote! { #lit => return Ok(#expr) });
- }
- Pattern::Ty(binding, ty) => {
- cast_checks.push(quote! {
- if <#ty as ::typst::eval::Reflect>::castable(&value) {
- let #binding = <#ty as ::typst::eval::FromValue>::from_value(value)?;
- return Ok(#expr);
- }
- });
- }
- }
- }
-
- let dynamic_check = input.name.is_some().then(|| {
- quote! {
- if let ::typst::eval::Value::Dyn(dynamic) = &value {
- if let Some(concrete) = dynamic.downcast::<Self>() {
- return Ok(concrete.clone());
- }
- }
- }
- });
-
- let str_check = (!string_arms.is_empty()).then(|| {
- quote! {
- if let ::typst::eval::Value::Str(string) = &value {
- match string.as_str() {
- #(#string_arms,)*
- _ => {}
- }
- }
- }
- });
-
- quote! {
- #dynamic_check
- #str_check
- #(#cast_checks)*
- Err(<Self as ::typst::eval::Reflect>::error(&value))
- }
-}
diff --git a/macros/src/element.rs b/macros/src/element.rs
deleted file mode 100644
index 6ce91fcb..00000000
--- a/macros/src/element.rs
+++ /dev/null
@@ -1,554 +0,0 @@
-use super::*;
-
-/// Expand the `#[element]` macro.
-pub fn element(stream: TokenStream, body: &syn::ItemStruct) -> Result<TokenStream> {
- let element = prepare(stream, body)?;
- Ok(create(&element))
-}
-
-struct Elem {
- name: String,
- display: String,
- category: String,
- keywords: Option<String>,
- docs: String,
- vis: syn::Visibility,
- ident: Ident,
- capable: Vec<Ident>,
- fields: Vec<Field>,
- scope: Option<BlockWithReturn>,
-}
-
-struct Field {
- name: String,
- docs: String,
- internal: bool,
- external: bool,
- positional: bool,
- required: bool,
- variadic: bool,
- synthesized: bool,
- fold: bool,
- resolve: bool,
- parse: Option<BlockWithReturn>,
- default: syn::Expr,
- vis: syn::Visibility,
- ident: Ident,
- ident_in: Ident,
- with_ident: Ident,
- push_ident: Ident,
- set_ident: Ident,
- ty: syn::Type,
- output: syn::Type,
-}
-
-impl Field {
- fn inherent(&self) -> bool {
- self.required || self.variadic
- }
-
- fn settable(&self) -> bool {
- !self.inherent()
- }
-}
-
-/// Preprocess the element's definition.
-fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result<Elem> {
- let syn::Fields::Named(named) = &body.fields else {
- bail!(body, "expected named fields");
- };
-
- let mut fields = vec![];
- for field in &named.named {
- let Some(ident) = field.ident.clone() else {
- bail!(field, "expected named field");
- };
-
- let mut attrs = field.attrs.clone();
- let variadic = has_attr(&mut attrs, "variadic");
- let required = has_attr(&mut attrs, "required") || variadic;
- let positional = has_attr(&mut attrs, "positional") || required;
-
- if ident == "label" {
- bail!(ident, "invalid field name");
- }
-
- let mut field = Field {
- name: kebab_case(&ident),
- docs: documentation(&attrs),
- internal: has_attr(&mut attrs, "internal"),
- external: has_attr(&mut attrs, "external"),
- positional,
- required,
- variadic,
- synthesized: has_attr(&mut attrs, "synthesized"),
- fold: has_attr(&mut attrs, "fold"),
- resolve: has_attr(&mut attrs, "resolve"),
- parse: parse_attr(&mut attrs, "parse")?.flatten(),
- default: parse_attr(&mut attrs, "default")?
- .flatten()
- .unwrap_or_else(|| parse_quote! { ::std::default::Default::default() }),
- vis: field.vis.clone(),
- ident: ident.clone(),
- ident_in: Ident::new(&format!("{}_in", ident), ident.span()),
- with_ident: Ident::new(&format!("with_{}", ident), ident.span()),
- push_ident: Ident::new(&format!("push_{}", ident), ident.span()),
- set_ident: Ident::new(&format!("set_{}", ident), ident.span()),
- ty: field.ty.clone(),
- output: field.ty.clone(),
- };
-
- if field.required && (field.fold || field.resolve) {
- bail!(ident, "required fields cannot be folded or resolved");
- }
-
- if field.required && !field.positional {
- bail!(ident, "only positional fields can be required");
- }
-
- if field.resolve {
- let output = &field.output;
- field.output = parse_quote! { <#output as ::typst::model::Resolve>::Output };
- }
- if field.fold {
- let output = &field.output;
- field.output = parse_quote! { <#output as ::typst::model::Fold>::Output };
- }
-
- validate_attrs(&attrs)?;
- fields.push(field);
- }
-
- let capable = Punctuated::<Ident, Token![,]>::parse_terminated
- .parse2(stream)?
- .into_iter()
- .collect();
-
- let mut attrs = body.attrs.clone();
- let docs = documentation(&attrs);
- let mut lines = docs.split('\n').collect();
- let keywords = meta_line(&mut lines, "Keywords").ok().map(Into::into);
- let category = meta_line(&mut lines, "Category")?.into();
- let display = meta_line(&mut lines, "Display")?.into();
- let docs = lines.join("\n").trim().into();
-
- let element = Elem {
- name: body.ident.to_string().trim_end_matches("Elem").to_lowercase(),
- display,
- category,
- keywords,
- docs,
- vis: body.vis.clone(),
- ident: body.ident.clone(),
- capable,
- fields,
- scope: parse_attr(&mut attrs, "scope")?.flatten(),
- };
-
- validate_attrs(&attrs)?;
- Ok(element)
-}
-
-/// Produce the element's definition.
-fn create(element: &Elem) -> TokenStream {
- let Elem { vis, ident, docs, .. } = element;
- let all = element.fields.iter().filter(|field| !field.external);
- let settable = all.clone().filter(|field| !field.synthesized && field.settable());
-
- // Inherent methods and functions.
- let new = create_new_func(element);
- let field_methods = all.clone().map(create_field_method);
- let field_in_methods = settable.clone().map(create_field_in_method);
- let with_field_methods = all.clone().map(create_with_field_method);
- let push_field_methods = all.map(create_push_field_method);
- let field_style_methods = settable.map(create_set_field_method);
-
- // Trait implementations.
- let element_impl = create_pack_impl(element);
- let construct_impl = element
- .capable
- .iter()
- .all(|capability| capability != "Construct")
- .then(|| create_construct_impl(element));
- let set_impl = create_set_impl(element);
- let locatable_impl = element
- .capable
- .iter()
- .any(|capability| capability == "Locatable")
- .then(|| quote! { impl ::typst::model::Locatable for #ident {} });
-
- quote! {
- #[doc = #docs]
- #[derive(Debug, Clone, Hash)]
- #[repr(transparent)]
- #vis struct #ident(pub ::typst::model::Content);
-
- impl #ident {
- #new
- #(#field_methods)*
- #(#field_in_methods)*
- #(#with_field_methods)*
- #(#push_field_methods)*
- #(#field_style_methods)*
-
- /// The element's span.
- pub fn span(&self) -> ::typst::syntax::Span {
- self.0.span()
- }
-
- /// Set the element's span.
- pub fn spanned(self, span: ::typst::syntax::Span) -> Self {
- Self(self.0.spanned(span))
- }
- }
-
- #element_impl
- #construct_impl
- #set_impl
- #locatable_impl
-
- impl ::typst::eval::IntoValue for #ident {
- fn into_value(self) -> ::typst::eval::Value {
- ::typst::eval::Value::Content(self.0)
- }
- }
- }
-}
-
-/// Create the `new` function for the element.
-fn create_new_func(element: &Elem) -> TokenStream {
- let relevant = element
- .fields
- .iter()
- .filter(|field| !field.external && !field.synthesized && field.inherent());
- let params = relevant.clone().map(|Field { ident, ty, .. }| {
- quote! { #ident: #ty }
- });
- let builder_calls = relevant.map(|Field { ident, with_ident, .. }| {
- quote! { .#with_ident(#ident) }
- });
- quote! {
- /// Create a new element.
- pub fn new(#(#params),*) -> Self {
- Self(::typst::model::Content::new(
- <Self as ::typst::model::Element>::func()
- ))
- #(#builder_calls)*
- }
- }
-}
-
-/// Create an accessor methods for a field.
-fn create_field_method(field: &Field) -> TokenStream {
- let Field { vis, docs, ident, name, output, .. } = field;
- if field.inherent() || field.synthesized {
- quote! {
- #[doc = #docs]
- #[track_caller]
- #vis fn #ident(&self) -> #output {
- self.0.expect_field(#name)
- }
- }
- } else {
- let access = create_style_chain_access(field, quote! { self.0.field(#name) });
- quote! {
- #[doc = #docs]
- #vis fn #ident(&self, styles: ::typst::model::StyleChain) -> #output {
- #access
- }
- }
- }
-}
-
-/// Create a style chain access method for a field.
-fn create_field_in_method(field: &Field) -> TokenStream {
- let Field { vis, ident_in, name, output, .. } = field;
- let doc = format!("Access the `{}` field in the given style chain.", name);
- let access = create_style_chain_access(field, quote! { None });
- quote! {
- #[doc = #doc]
- #vis fn #ident_in(styles: ::typst::model::StyleChain) -> #output {
- #access
- }
- }
-}
-
-/// Create a style chain access method for a field.
-fn create_style_chain_access(field: &Field, inherent: TokenStream) -> TokenStream {
- let Field { name, ty, default, .. } = field;
- let getter = match (field.fold, field.resolve) {
- (false, false) => quote! { get },
- (false, true) => quote! { get_resolve },
- (true, false) => quote! { get_fold },
- (true, true) => quote! { get_resolve_fold },
- };
-
- quote! {
- styles.#getter::<#ty>(
- <Self as ::typst::model::Element>::func(),
- #name,
- #inherent,
- || #default,
- )
- }
-}
-
-/// Create a builder pattern method for a field.
-fn create_with_field_method(field: &Field) -> TokenStream {
- let Field { vis, ident, with_ident, name, ty, .. } = field;
- let doc = format!("Set the [`{}`](Self::{}) field.", name, ident);
- quote! {
- #[doc = #doc]
- #vis fn #with_ident(mut self, #ident: #ty) -> Self {
- Self(self.0.with_field(#name, #ident))
- }
- }
-}
-
-/// Create a set-style method for a field.
-fn create_push_field_method(field: &Field) -> TokenStream {
- let Field { vis, ident, push_ident, name, ty, .. } = field;
- let doc = format!("Push the [`{}`](Self::{}) field.", name, ident);
- quote! {
- #[doc = #doc]
- #vis fn #push_ident(&mut self, #ident: #ty) {
- self.0.push_field(#name, #ident);
- }
- }
-}
-
-/// Create a setter method for a field.
-fn create_set_field_method(field: &Field) -> TokenStream {
- let Field { vis, ident, set_ident, name, ty, .. } = field;
- let doc = format!("Create a style property for the `{}` field.", name);
- quote! {
- #[doc = #doc]
- #vis fn #set_ident(#ident: #ty) -> ::typst::model::Style {
- ::typst::model::Style::Property(::typst::model::Property::new(
- <Self as ::typst::model::Element>::func(),
- #name,
- #ident,
- ))
- }
- }
-}
-
-/// Create the element's `Pack` implementation.
-fn create_pack_impl(element: &Elem) -> TokenStream {
- let Elem { ident, name, display, keywords, category, docs, .. } = element;
- let vtable_func = create_vtable_func(element);
- let infos = element
- .fields
- .iter()
- .filter(|field| !field.internal && !field.synthesized)
- .map(create_param_info);
- let scope = create_scope_builder(element.scope.as_ref());
- let keywords = quote_option(keywords);
- quote! {
- impl ::typst::model::Element for #ident {
- fn pack(self) -> ::typst::model::Content {
- self.0
- }
-
- fn unpack(content: &::typst::model::Content) -> ::std::option::Option<&Self> {
- // Safety: Elements are #[repr(transparent)].
- content.is::<Self>().then(|| unsafe {
- ::std::mem::transmute(content)
- })
- }
-
- fn func() -> ::typst::model::ElemFunc {
- static NATIVE: ::typst::model::NativeElemFunc = ::typst::model::NativeElemFunc {
- name: #name,
- vtable: #vtable_func,
- construct: <#ident as ::typst::model::Construct>::construct,
- set: <#ident as ::typst::model::Set>::set,
- info: ::typst::eval::Lazy::new(|| typst::eval::FuncInfo {
- name: #name,
- display: #display,
- keywords: #keywords,
- docs: #docs,
- params: ::std::vec![#(#infos),*],
- returns: ::typst::eval::CastInfo::Union(::std::vec![
- ::typst::eval::CastInfo::Type("content")
- ]),
- category: #category,
- scope: #scope,
- }),
- };
- (&NATIVE).into()
- }
- }
- }
-}
-
-/// Create the element's casting vtable.
-fn create_vtable_func(element: &Elem) -> TokenStream {
- let ident = &element.ident;
- let relevant = element.capable.iter().filter(|&ident| ident != "Construct");
- let checks = relevant.map(|capability| {
- quote! {
- if id == ::std::any::TypeId::of::<dyn #capability>() {
- return Some(unsafe {
- ::typst::util::fat::vtable(&null as &dyn #capability)
- });
- }
- }
- });
-
- quote! {
- |id| {
- let null = Self(::typst::model::Content::new(
- <#ident as ::typst::model::Element>::func()
- ));
- #(#checks)*
- None
- }
- }
-}
-
-/// Create a parameter info for a field.
-fn create_param_info(field: &Field) -> TokenStream {
- let Field {
- name,
- docs,
- positional,
- variadic,
- required,
- default,
- fold,
- ty,
- output,
- ..
- } = field;
- let named = !positional;
- let settable = field.settable();
- let default_ty = if *fold { &output } else { &ty };
- let default = quote_option(&settable.then(|| {
- quote! {
- || {
- let typed: #default_ty = #default;
- ::typst::eval::IntoValue::into_value(typed)
- }
- }
- }));
- let ty = if *variadic {
- quote! { <#ty as ::typst::eval::Variadics>::Inner }
- } else {
- quote! { #ty }
- };
- quote! {
- ::typst::eval::ParamInfo {
- name: #name,
- docs: #docs,
- cast: <#ty as ::typst::eval::Reflect>::describe(),
- default: #default,
- positional: #positional,
- named: #named,
- variadic: #variadic,
- required: #required,
- settable: #settable,
- }
- }
-}
-
-/// Create the element's `Construct` implementation.
-fn create_construct_impl(element: &Elem) -> TokenStream {
- let ident = &element.ident;
- let handlers = element
- .fields
- .iter()
- .filter(|field| {
- !field.external
- && !field.synthesized
- && (!field.internal || field.parse.is_some())
- })
- .map(|field| {
- let push_ident = &field.push_ident;
- let (prefix, value) = create_field_parser(field);
- if field.settable() {
- quote! {
- #prefix
- if let Some(value) = #value {
- element.#push_ident(value);
- }
- }
- } else {
- quote! {
- #prefix
- element.#push_ident(#value);
- }
- }
- });
-
- quote! {
- impl ::typst::model::Construct for #ident {
- fn construct(
- vm: &mut ::typst::eval::Vm,
- args: &mut ::typst::eval::Args,
- ) -> ::typst::diag::SourceResult<::typst::model::Content> {
- let mut element = Self(::typst::model::Content::new(
- <Self as ::typst::model::Element>::func()
- ));
- #(#handlers)*
- Ok(element.0)
- }
- }
- }
-}
-
-/// Create the element's `Set` implementation.
-fn create_set_impl(element: &Elem) -> TokenStream {
- let ident = &element.ident;
- let handlers = element
- .fields
- .iter()
- .filter(|field| {
- !field.external
- && !field.synthesized
- && field.settable()
- && (!field.internal || field.parse.is_some())
- })
- .map(|field| {
- let set_ident = &field.set_ident;
- let (prefix, value) = create_field_parser(field);
- quote! {
- #prefix
- if let Some(value) = #value {
- styles.set(Self::#set_ident(value));
- }
- }
- });
-
- quote! {
- impl ::typst::model::Set for #ident {
- fn set(
- args: &mut ::typst::eval::Args,
- ) -> ::typst::diag::SourceResult<::typst::model::Styles> {
- let mut styles = ::typst::model::Styles::new();
- #(#handlers)*
- Ok(styles)
- }
- }
- }
-}
-
-/// Create argument parsing code for a field.
-fn create_field_parser(field: &Field) -> (TokenStream, TokenStream) {
- if let Some(BlockWithReturn { prefix, expr }) = &field.parse {
- return (quote! { #(#prefix);* }, quote! { #expr });
- }
-
- let name = &field.name;
- let value = if field.variadic {
- quote! { args.all()? }
- } else if field.required {
- quote! { args.expect(#name)? }
- } else if field.positional {
- quote! { args.find()? }
- } else {
- quote! { args.named(#name)? }
- };
-
- (quote! {}, value)
-}
diff --git a/macros/src/func.rs b/macros/src/func.rs
deleted file mode 100644
index 4a68e846..00000000
--- a/macros/src/func.rs
+++ /dev/null
@@ -1,268 +0,0 @@
-use super::*;
-
-/// Expand the `#[func]` macro.
-pub fn func(stream: TokenStream, item: &syn::ItemFn) -> Result<TokenStream> {
- let func = prepare(stream, item)?;
- Ok(create(&func, item))
-}
-
-struct Func {
- name: String,
- display: String,
- category: String,
- keywords: Option<String>,
- docs: String,
- vis: syn::Visibility,
- ident: Ident,
- ident_func: Ident,
- parent: Option<syn::Type>,
- vm: bool,
- vt: bool,
- args: bool,
- span: bool,
- params: Vec<Param>,
- returns: syn::Type,
- scope: Option<BlockWithReturn>,
-}
-
-struct Param {
- name: String,
- docs: String,
- external: bool,
- named: bool,
- variadic: bool,
- default: Option<syn::Expr>,
- ident: Ident,
- ty: syn::Type,
-}
-
-fn prepare(stream: TokenStream, item: &syn::ItemFn) -> Result<Func> {
- let sig = &item.sig;
-
- let Parent(parent) = syn::parse2(stream)?;
-
- let mut vm = false;
- let mut vt = false;
- let mut args = false;
- let mut span = false;
- let mut params = vec![];
- for input in &sig.inputs {
- let syn::FnArg::Typed(typed) = input else {
- bail!(input, "self is not allowed here");
- };
-
- let syn::Pat::Ident(syn::PatIdent {
- by_ref: None,
- mutability: None,
- ident,
- ..
- }) = &*typed.pat else {
- bail!(typed.pat, "expected identifier");
- };
-
- match ident.to_string().as_str() {
- "vm" => vm = true,
- "vt" => vt = true,
- "args" => args = true,
- "span" => span = true,
- _ => {
- let mut attrs = typed.attrs.clone();
- params.push(Param {
- name: kebab_case(ident),
- docs: documentation(&attrs),
- external: has_attr(&mut attrs, "external"),
- named: has_attr(&mut attrs, "named"),
- variadic: has_attr(&mut attrs, "variadic"),
- default: parse_attr(&mut attrs, "default")?.map(|expr| {
- expr.unwrap_or_else(
- || parse_quote! { ::std::default::Default::default() },
- )
- }),
- ident: ident.clone(),
- ty: (*typed.ty).clone(),
- });
-
- validate_attrs(&attrs)?;
- }
- }
- }
-
- let mut attrs = item.attrs.clone();
- let docs = documentation(&attrs);
- let mut lines = docs.split('\n').collect();
- let keywords = meta_line(&mut lines, "Keywords").ok().map(Into::into);
- let category = meta_line(&mut lines, "Category")?.into();
- let display = meta_line(&mut lines, "Display")?.into();
- let docs = lines.join("\n").trim().into();
-
- let func = Func {
- name: sig.ident.to_string().trim_end_matches('_').replace('_', "-"),
- display,
- category,
- keywords,
- docs,
- vis: item.vis.clone(),
- ident: sig.ident.clone(),
- ident_func: Ident::new(
- &format!("{}_func", sig.ident.to_string().trim_end_matches('_')),
- sig.ident.span(),
- ),
- parent,
- params,
- returns: match &sig.output {
- syn::ReturnType::Default => parse_quote! { () },
- syn::ReturnType::Type(_, ty) => ty.as_ref().clone(),
- },
- scope: parse_attr(&mut attrs, "scope")?.flatten(),
- vm,
- vt,
- args,
- span,
- };
-
- Ok(func)
-}
-
-fn create(func: &Func, item: &syn::ItemFn) -> TokenStream {
- let Func {
- name,
- display,
- category,
- docs,
- vis,
- ident,
- ident_func,
- returns,
- ..
- } = func;
-
- let handlers = func
- .params
- .iter()
- .filter(|param| !param.external)
- .map(create_param_parser);
-
- let args = func
- .params
- .iter()
- .filter(|param| !param.external)
- .map(|param| &param.ident);
-
- let parent = func.parent.as_ref().map(|ty| quote! { #ty:: });
- let vm_ = func.vm.then(|| quote! { vm, });
- let vt_ = func.vt.then(|| quote! { &mut vm.vt, });
- let args_ = func.args.then(|| quote! { args.take(), });
- let span_ = func.span.then(|| quote! { args.span, });
- let wrapper = quote! {
- |vm, args| {
- let __typst_func = #parent #ident;
- #(#handlers)*
- let output = __typst_func(#(#args,)* #vm_ #vt_ #args_ #span_);
- ::typst::eval::IntoResult::into_result(output, args.span)
- }
- };
-
- let mut item = item.clone();
- item.attrs.clear();
-
- let inputs = item.sig.inputs.iter().cloned().filter_map(|mut input| {
- if let syn::FnArg::Typed(typed) = &mut input {
- if typed.attrs.iter().any(|attr| attr.path().is_ident("external")) {
- return None;
- }
- typed.attrs.clear();
- }
- Some(input)
- });
-
- item.sig.inputs = parse_quote! { #(#inputs),* };
-
- let keywords = quote_option(&func.keywords);
- let params = func.params.iter().map(create_param_info);
- let scope = create_scope_builder(func.scope.as_ref());
-
- quote! {
- #[doc(hidden)]
- #vis fn #ident_func() -> &'static ::typst::eval::NativeFunc {
- static FUNC: ::typst::eval::NativeFunc = ::typst::eval::NativeFunc {
- func: #wrapper,
- info: ::typst::eval::Lazy::new(|| typst::eval::FuncInfo {
- name: #name,
- display: #display,
- keywords: #keywords,
- category: #category,
- docs: #docs,
- params: ::std::vec![#(#params),*],
- returns: <#returns as ::typst::eval::Reflect>::describe(),
- scope: #scope,
- }),
- };
- &FUNC
- }
-
- #[doc = #docs]
- #item
- }
-}
-
-/// Create a parameter info for a field.
-fn create_param_info(param: &Param) -> TokenStream {
- let Param { name, docs, named, variadic, ty, default, .. } = param;
- let positional = !named;
- let required = default.is_none();
- let default = quote_option(&default.as_ref().map(|_default| {
- quote! {
- || {
- let typed: #ty = #default;
- ::typst::eval::IntoValue::into_value(typed)
- }
- }
- }));
- let ty = if *variadic {
- quote! { <#ty as ::typst::eval::Variadics>::Inner }
- } else {
- quote! { #ty }
- };
- quote! {
- ::typst::eval::ParamInfo {
- name: #name,
- docs: #docs,
- cast: <#ty as ::typst::eval::Reflect>::describe(),
- default: #default,
- positional: #positional,
- named: #named,
- variadic: #variadic,
- required: #required,
- settable: false,
- }
- }
-}
-
-/// Create argument parsing code for a parameter.
-fn create_param_parser(param: &Param) -> TokenStream {
- let Param { name, ident, ty, .. } = param;
-
- let mut value = if param.variadic {
- quote! { args.all()? }
- } else if param.named {
- quote! { args.named(#name)? }
- } else if param.default.is_some() {
- quote! { args.eat()? }
- } else {
- quote! { args.expect(#name)? }
- };
-
- if let Some(default) = &param.default {
- value = quote! { #value.unwrap_or_else(|| #default) }
- }
-
- quote! { let mut #ident: #ty = #value; }
-}
-
-struct Parent(Option<syn::Type>);
-
-impl Parse for Parent {
- fn parse(input: ParseStream) -> Result<Self> {
- Ok(Self(if !input.is_empty() { Some(input.parse()?) } else { None }))
- }
-}
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
deleted file mode 100644
index 49840ef2..00000000
--- a/macros/src/lib.rs
+++ /dev/null
@@ -1,63 +0,0 @@
-//! Procedural macros for Typst.
-
-extern crate proc_macro;
-
-#[macro_use]
-mod util;
-mod castable;
-mod element;
-mod func;
-mod symbols;
-
-use proc_macro::TokenStream as BoundaryStream;
-use proc_macro2::TokenStream;
-use quote::quote;
-use syn::ext::IdentExt;
-use syn::parse::{Parse, ParseStream, Parser};
-use syn::punctuated::Punctuated;
-use syn::{parse_quote, DeriveInput, Ident, Result, Token};
-
-use self::util::*;
-
-/// Turns a function into a `NativeFunc`.
-#[proc_macro_attribute]
-pub fn func(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
- let item = syn::parse_macro_input!(item as syn::ItemFn);
- func::func(stream.into(), &item)
- .unwrap_or_else(|err| err.to_compile_error())
- .into()
-}
-
-/// Turns a type into an `Element`.
-#[proc_macro_attribute]
-pub fn element(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
- let item = syn::parse_macro_input!(item as syn::ItemStruct);
- element::element(stream.into(), &item)
- .unwrap_or_else(|err| err.to_compile_error())
- .into()
-}
-
-/// Implements `Reflect`, `FromValue`, and `IntoValue` for an enum.
-#[proc_macro_derive(Cast, attributes(string))]
-pub fn derive_cast(item: BoundaryStream) -> BoundaryStream {
- let item = syn::parse_macro_input!(item as DeriveInput);
- castable::derive_cast(&item)
- .unwrap_or_else(|err| err.to_compile_error())
- .into()
-}
-
-/// Implements `Reflect`, `FromValue`, and `IntoValue` for a type.
-#[proc_macro]
-pub fn cast(stream: BoundaryStream) -> BoundaryStream {
- castable::cast(stream.into())
- .unwrap_or_else(|err| err.to_compile_error())
- .into()
-}
-
-/// Defines a list of `Symbol`s.
-#[proc_macro]
-pub fn symbols(stream: BoundaryStream) -> BoundaryStream {
- symbols::symbols(stream.into())
- .unwrap_or_else(|err| err.to_compile_error())
- .into()
-}
diff --git a/macros/src/symbols.rs b/macros/src/symbols.rs
deleted file mode 100644
index cdb7f5d7..00000000
--- a/macros/src/symbols.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-use super::*;
-
-/// Expand the `symbols!` macro.
-pub fn symbols(stream: TokenStream) -> Result<TokenStream> {
- let list: Punctuated<Symbol, Token![,]> =
- Punctuated::parse_terminated.parse2(stream)?;
- let pairs = list.iter().map(|symbol| {
- let name = symbol.name.to_string();
- let kind = match &symbol.kind {
- Kind::Single(c) => quote! { typst::eval::Symbol::new(#c), },
- Kind::Multiple(variants) => {
- let variants = variants.iter().map(|variant| {
- let name = &variant.name;
- let c = &variant.c;
- quote! { (#name, #c) }
- });
- quote! {
- typst::eval::Symbol::list(&[#(#variants),*])
- }
- }
- };
- quote! { (#name, #kind) }
- });
- Ok(quote! { &[#(#pairs),*] })
-}
-
-struct Symbol {
- name: syn::Ident,
- kind: Kind,
-}
-
-enum Kind {
- Single(syn::LitChar),
- Multiple(Punctuated<Variant, Token![,]>),
-}
-
-struct Variant {
- name: String,
- c: syn::LitChar,
-}
-
-impl Parse for Symbol {
- fn parse(input: ParseStream) -> Result<Self> {
- let name = input.call(Ident::parse_any)?;
- input.parse::<Token![:]>()?;
- let kind = input.parse()?;
- Ok(Self { name, kind })
- }
-}
-
-impl Parse for Kind {
- fn parse(input: ParseStream) -> Result<Self> {
- if input.peek(syn::LitChar) {
- Ok(Self::Single(input.parse()?))
- } else {
- let content;
- syn::bracketed!(content in input);
- Ok(Self::Multiple(Punctuated::parse_terminated(&content)?))
- }
- }
-}
-
-impl Parse for Variant {
- fn parse(input: ParseStream) -> Result<Self> {
- let mut name = String::new();
- if input.peek(syn::Ident::peek_any) {
- name.push_str(&input.call(Ident::parse_any)?.to_string());
- while input.peek(Token![.]) {
- input.parse::<Token![.]>()?;
- name.push('.');
- name.push_str(&input.call(Ident::parse_any)?.to_string());
- }
- input.parse::<Token![:]>()?;
- }
- let c = input.parse()?;
- Ok(Self { name, c })
- }
-}
diff --git a/macros/src/util.rs b/macros/src/util.rs
deleted file mode 100644
index 389fed06..00000000
--- a/macros/src/util.rs
+++ /dev/null
@@ -1,145 +0,0 @@
-use heck::ToKebabCase;
-use quote::ToTokens;
-
-use super::*;
-
-/// Return an error at the given item.
-macro_rules! bail {
- (callsite, $($tts:tt)*) => {
- return Err(syn::Error::new(
- proc_macro2::Span::call_site(),
- format!("typst: {}", format!($($tts)*))
- ))
- };
- ($item:expr, $($tts:tt)*) => {
- return Err(syn::Error::new_spanned(
- &$item,
- format!("typst: {}", format!($($tts)*))
- ))
- };
-}
-
-/// 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()
-}
-
-/// Whether an attribute list has a specified attribute.
-pub fn parse_attr<T: Parse>(
- attrs: &mut Vec<syn::Attribute>,
- target: &str,
-) -> Result<Option<Option<T>>> {
- take_attr(attrs, target)
- .map(|attr| {
- Ok(match attr.meta {
- syn::Meta::Path(_) => None,
- syn::Meta::List(list) => Some(list.parse_args()?),
- syn::Meta::NameValue(meta) => bail!(meta, "not valid here"),
- })
- })
- .transpose()
-}
-
-/// Whether an attribute list has a specified attribute.
-pub fn take_attr(
- attrs: &mut Vec<syn::Attribute>,
- target: &str,
-) -> Option<syn::Attribute> {
- attrs
- .iter()
- .position(|attr| attr.path().is_ident(target))
- .map(|i| attrs.remove(i))
-}
-
-/// Ensure that no unrecognized attributes remain.
-pub fn validate_attrs(attrs: &[syn::Attribute]) -> Result<()> {
- for attr in attrs {
- if !attr.path().is_ident("doc") && !attr.path().is_ident("derive") {
- let ident = attr.path().get_ident().unwrap();
- bail!(ident, "unrecognized attribute: {ident}");
- }
- }
- Ok(())
-}
-
-/// Convert an identifier to a kebab-case string.
-pub fn kebab_case(name: &Ident) -> String {
- name.to_string().to_kebab_case()
-}
-
-/// Extract documentation comments from an attribute list.
-pub fn documentation(attrs: &[syn::Attribute]) -> String {
- let mut doc = String::new();
-
- // Parse doc comments.
- for attr in attrs {
- if let syn::Meta::NameValue(meta) = &attr.meta {
- if meta.path.is_ident("doc") {
- if let syn::Expr::Lit(lit) = &meta.value {
- if let syn::Lit::Str(string) = &lit.lit {
- let full = string.value();
- let line = full.strip_prefix(' ').unwrap_or(&full);
- doc.push_str(line);
- doc.push('\n');
- }
- }
- }
- }
- }
-
- doc.trim().into()
-}
-
-/// Extract a line of metadata from documentation.
-pub fn meta_line<'a>(lines: &mut Vec<&'a str>, key: &str) -> Result<&'a str> {
- match lines.last().and_then(|line| line.strip_prefix(&format!("{key}:"))) {
- Some(value) => {
- lines.pop();
- Ok(value.trim())
- }
- 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() }
- }
-}
-
-/// Quotes an option literally.
-pub fn quote_option<T: ToTokens>(option: &Option<T>) -> TokenStream {
- if let Some(value) = option {
- quote! { Some(#value) }
- } else {
- quote! { None }
- }
-}