diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-02-02 18:09:13 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-02-02 19:50:06 +0100 |
| commit | 10675cd6d510521715c5e33508d80c4193ad9d74 (patch) | |
| tree | 676435532b2c224b4b9feafd6e1a07cd30a0e923 /macros | |
| parent | e9ff2d6463bf26cb0bbafb747bf8a77800687e3f (diff) | |
Merge text and math symbols
Diffstat (limited to 'macros')
| -rw-r--r-- | macros/src/lib.rs | 9 | ||||
| -rw-r--r-- | macros/src/symbols.rs | 104 |
2 files changed, 113 insertions, 0 deletions
diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 064e45b2..e162745a 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -22,6 +22,7 @@ mod capable; mod castable; mod func; mod node; +mod symbols; use proc_macro::TokenStream as BoundaryStream; use proc_macro2::{TokenStream, TokenTree}; @@ -68,6 +69,14 @@ pub fn castable(stream: BoundaryStream) -> BoundaryStream { .into() } +/// Define a list of symbols. +#[proc_macro] +pub fn symbols(stream: BoundaryStream) -> BoundaryStream { + symbols::symbols(stream.into()) + .unwrap_or_else(|err| err.to_compile_error()) + .into() +} + /// Extract documentation comments from an attribute list. fn documentation(attrs: &[syn::Attribute]) -> String { let mut doc = String::new(); diff --git a/macros/src/symbols.rs b/macros/src/symbols.rs new file mode 100644 index 00000000..fb120883 --- /dev/null +++ b/macros/src/symbols.rs @@ -0,0 +1,104 @@ +use syn::ext::IdentExt; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::Token; + +use super::*; + +/// Expand the `symbols!` macro. +pub fn symbols(stream: TokenStream) -> Result<TokenStream> { + let list: List = syn::parse2(stream)?; + let pairs = list.0.iter().map(Symbol::expand); + Ok(quote! { &[#(#pairs),*] }) +} + +struct List(Punctuated<Symbol, Token![,]>); + +impl Parse for List { + fn parse(input: ParseStream) -> Result<Self> { + Punctuated::parse_terminated(input).map(Self) + } +} + +struct Symbol { + name: syn::Ident, + kind: Kind, +} + +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 Symbol { + fn expand(&self) -> TokenStream { + let name = self.name.to_string(); + let kind = self.kind.expand(); + quote! { (#name, #kind) } + } +} + +enum Kind { + Single(syn::LitChar), + Multiple(Punctuated<Variant, Token![,]>), +} + +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 Kind { + fn expand(&self) -> TokenStream { + match self { + Self::Single(c) => quote! { typst::model::Symbol::new(#c), }, + Self::Multiple(variants) => { + let variants = variants.iter().map(Variant::expand); + quote! { + typst::model::Symbol::list(&[#(#variants),*]) + } + } + } + } +} + +struct Variant { + name: String, + c: syn::LitChar, +} + +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 }) + } +} + +impl Variant { + fn expand(&self) -> TokenStream { + let name = &self.name; + let c = &self.c; + quote! { (#name, #c) } + } +} |
