laburnum-syntax-macro 0.1.0

Proc-macros for defining CST and AST node types in language frontends built with the laburnum LSP framework.
Documentation
// Copyright Two Neutron Stars Incorporated and contributors
// SPDX-License-Identifier: BlueOak-1.0.0

use {
  proc_macro_error::proc_macro_error,
  proc_macro2::TokenStream,
};

mod args;
use args::Args;
mod ast;
mod cst;
mod error;

#[proc_macro_attribute]
#[proc_macro_error]
pub fn laburnum_syntax(
  args: proc_macro::TokenStream,
  item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
  process(args.into(), item.into())
    .unwrap_or_else(|syn_error| {
      // Return the compile error token stream directly
      syn_error.into_compile_error()
    })
    .into()
}

pub(crate) fn process(
  attr: TokenStream,
  item: TokenStream,
) -> Result<TokenStream, syn::Error> {
  // First we parse the flags from the attribute attr (if any)
  let f = Args::parse(attr.clone());

  // Validate that exactly one syntax type is specified
  match (f.is_cst(), f.is_ast()) {
    | (true, false) => cst::process(item, f),
    | (false, true) => ast::process(item, f),
    | (true, true) => {
      // Both CST and AST specified - this is an error
      Err(syn::Error::new(
        f.conflicting_span(),
        "Cannot specify both AST and CST flags. Choose either AST or CST, not both: #[laburnum_syntax(AST)] or #[laburnum_syntax(CST)].",
      ))
    },
    | (false, false) => {
      // Neither specified - provide helpful error
      Err(syn::Error::new(
        f.missing_type_span(),
        "Missing syntax type specification. Add either AST or CST to the attribute: #[laburnum_syntax(AST)] or #[laburnum_syntax(CST)].",
      ))
    },
  }
}

#[cfg(test)]
mod tests;