scopegraphs-macros 0.3.0

A port of scopegraphs (https://pl.ewi.tudelft.nl/research/projects/scope-graphs/) to Rust
Documentation
use std::rc::Rc;

use proc_macro::TokenStream;
use scopegraphs_regular_expressions::Regex;
use syn::parse::{Parse, ParseStream};
use syn::{Attribute, Ident, Meta, Token, Type};

#[cfg(feature = "dot")]
use std::fs::File;
#[cfg(feature = "dot")]
use syn::{Expr, ExprLit, Lit, MetaNameValue};

pub(crate) struct RegexInput {
    attrs: Vec<Attribute>,
    _type: Token![type],
    name: Ident,
    _open: Token![<],
    alphabet_type: Type,
    _close: Token![>],
    _equals: Token![=],
    regex: Regex,
    errors: Vec<syn::Error>,
}

impl Parse for RegexInput {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let attrs = input.call(Attribute::parse_outer)?;
        let _type = input.parse()?;
        let name = input.parse()?;
        let _open = input.parse()?;
        let alphabet_type = input.parse()?;
        let _close = input.parse()?;
        let _equals = input.parse()?;
        let (regex, errors) = match input.parse() {
            Ok(re) => (re, vec![]),
            Err(err) => (Regex::Complement(Rc::new(Regex::EmptySet)), vec![err]),
        };
        Ok(Self {
            attrs,
            _type,
            name,
            _open,
            alphabet_type,
            _close,
            _equals,
            regex,
            errors,
        })
    }
}

impl RegexInput {
    pub fn compile(self) -> TokenStream {
        let mut errors = self.errors;
        #[cfg(feature = "dot")]
        let mut graph = None;

        for i in self.attrs {
            let attr = i.meta.clone();
            match attr {
                #[cfg(feature = "dot")]
                Meta::NameValue(MetaNameValue {
                    path,
                    value:
                        Expr::Lit(ExprLit {
                            lit: Lit::Str(s), ..
                        }),
                    ..
                }) if path.is_ident("graph") => {
                    graph = Some(s);
                }
                i => errors.push(syn::Error::new_spanned(i, "unexpected attribute")),
            }
        }

        let compiled = self.regex.compile();

        #[cfg(feature = "dot")]
        if let Some(path) = graph {
            let path = path.value();
            let mut f = File::create(&path)
                .unwrap_or_else(|e| panic!("can't open dot file for graphing at {path}: {e}"));
            compiled
                .render(&mut f)
                .unwrap_or_else(|e| panic!("failed while graphing at {path}: {e}"));
        }

        compiled
            .emit(&self.name, &self.alphabet_type, errors)
            .into()
    }
}