summaryrefslogtreecommitdiff
path: root/macros
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-02-02 18:09:13 +0100
committerLaurenz <laurmaedje@gmail.com>2023-02-02 19:50:06 +0100
commit10675cd6d510521715c5e33508d80c4193ad9d74 (patch)
tree676435532b2c224b4b9feafd6e1a07cd30a0e923 /macros
parente9ff2d6463bf26cb0bbafb747bf8a77800687e3f (diff)
Merge text and math symbols
Diffstat (limited to 'macros')
-rw-r--r--macros/src/lib.rs9
-rw-r--r--macros/src/symbols.rs104
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) }
+ }
+}