1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
//! This crate implements proc macros for the `emacs` crate. It is a dependency of `emacs` crate,
//! and should not be listed as a direct dependency by Emacs dynamic modules.

extern crate proc_macro;

use proc_macro::TokenStream;

use syn::{self, AttributeArgs, ItemFn, parse_macro_input};

mod util;
mod module;
mod func;

/// Registers a function as the initializer, to be called when Emacs loads the module. Each dynamic
/// module must have one and only one such function.
///
/// # Options
///
/// - `name`: By default, name of the feature provided by the module is the crate's name (with `_`
/// replaced by `-`). There is no need to explicitly call `provide` inside the initializer. This
/// option allows the initializer's name, or a string, to be used instead. For examples:
/// `#[module(name(fn))]`, or `#[module(name = "feature-name")]`.
/// - `separator`: Function names in Emacs are conventionally prefixed with the feature name,
/// followed by `-`, this option allows a different separator to be used. For example:
/// `#[module(separator = "/")]`.
/// - `mod_in_name`: Whether to put module path in function names. Default to `true`. This can also
/// be overridden for each individual function, by an option of the same name in [`#[defun]`].
///
/// [`#[defun]`]: attr.defun.html
#[proc_macro_attribute]
pub fn module(attr_ts: TokenStream, item_ts: TokenStream) -> TokenStream {
    let attr_args: AttributeArgs = parse_macro_input!(attr_ts);
    let fn_item: ItemFn = parse_macro_input!(item_ts);
    match module::Module::parse(attr_args, fn_item) {
        Ok(module) => module.render().into(),
        Err(e) => e.into(),
    }
}

/// Exports a function to the Lisp runtime. The function is bound when the module is loaded, even if
/// it is defined inside another function which is never called.
///
/// # Input Parameters
///
/// Each parameter must be one of the following:
///
/// - An owned value of a type that implements [`FromLisp`]. This is for simple data types that have
/// an equivalent in Lisp. Examples: `i64`, `String`, `bool`.
///
/// - A shared/mutable reference. This gives access to data structures that other module functions
/// have created and embedded in the Lisp runtime (through `user-ptr` objects).
///
/// - A Lisp [`Value`]. This allows holding off the conversion to Rust data structures until
/// necessary, or working with values that don't have a meaningful representation in Rust, like Lisp
/// lambdas.
///
/// - An [`&Env`]. This enables interaction with the Lisp runtime. It does not appear in the
/// function's Lisp signature. This is unnecessary if there is already another parameter with type
/// [`Value`], which allows accessing the runtime through `Value.env`.
///
/// # Return Value
///
/// The return type must be [`Result<T>`], where `T` is one of the following:
///
/// - A type that implements [`IntoLisp`]. This is for simple data types that have an equivalent in
/// Lisp. Example: `i64`, `String`, `bool`.
///
/// - A type that implements `Transfer`. This allows embedding a native data structure in a
/// `user-ptr` object, for read-only use cases. It requires `user_ptr(direct)` option to be
/// specified.
///
/// - An arbitrary type. This allows embedding a native data structure in a `user-ptr` object, for
/// read-write use cases. It requires `user_ptr` option to be specified. If the data is to be shared
/// with background Rust threads, `user_ptr(rwlock)` or `user_ptr(mutex)` must be used instead.
///
/// - [`Value`]. This is mostly useful for returning an input parameter unchanged.
///
/// # Naming
///
/// By default, the function's Lisp name has the form `<feature-prefix>[mod-prefix]<base-name>`.
/// - `feature-prefix` is the feature's name, followed by `-`. This can be customized by the `name`
/// and `separator` options on [`#[module]`].
///
/// - `mod-prefix` is constructed from the function's Rust module path (with `_` replaced by `-`).
/// This can be turned off crate-wide, or for individual function, using the option `mod_in_name`.
///
/// - `base-name` is the function's Rust name (with `_` replaced by `-`). This can be overridden
/// with the option `name`, e.g. `#[defun(name = "foo:bar")]`.
///
/// [`#[module]`]: attr.module.html
/// [`Result<T>`]: /emacs/*/emacs/type.Result.html
/// [`FromLisp`]: /emacs/*/emacs/trait.FromLisp.html
/// [`IntoLisp`]: /emacs/*/emacs/trait.IntoLisp.html
/// [`Transfer`]: /emacs/*/emacs/trait.Transfer.html
/// [`&Env`]: /emacs/*/emacs/struct.Env.html
/// [`Value`]: /emacs/*/emacs/struct.Value.html
#[proc_macro_attribute]
pub fn defun(attr_ts: TokenStream, item_ts: TokenStream) -> TokenStream {
    let attr_args: AttributeArgs = parse_macro_input!(attr_ts);
    let fn_item: ItemFn = parse_macro_input!(item_ts);
    match func::LispFunc::parse(attr_args, fn_item) {
        Ok(func) => func.render().into(),
        Err(e) => e.into(),
    }
}