emacs25からdynamic moduleという機能があるようです。
折角なので、rustを使ってその機能を利用してみようと思います。
参考
用意するもの
- rust(1.9.0)
- emacs(25.x)
emacs
$ git clone -b emacs-25 https://github.com/emacs-mirror/emacs.git --depth 1
$ ./autogen.sh
$ ./configure --with-modules
$ make
Cargo+Rust
cratesにemacsというそれっぽいものがありました。
Cargo.toml
[package]
name = "emacs-rust-fib"
[lib]
path = "src/lib.rs"
crate-type = ["dylib"]
[dependencies]
libc = "*"
emacs = "*"
src/lib.rs
#![allow(non_snake_case, non_camel_case_types)]
extern crate libc;
extern crate emacs;
use std::os::raw::{c_int, c_void, c_char};
use std::ptr::null_mut;
use std::ffi::{CString, CStr};
use emacs::{get_environment, provide, bind_function, make_function, find_function};
use emacs::emacs_module::{emacs_env, emacs_value, intmax_t, ptrdiff_t, Struct_emacs_runtime};
// フィボナッチ数
fn fib(i: intmax_t) -> intmax_t {
match i {
0 | 1 => i,
n => fib(n-1) + fib(n-2),
}
}
unsafe extern fn f_fib(
env: *mut emacs_env,
_: ptrdiff_t,
args: *mut emacs_value,
_: *mut c_void
) -> emacs_value {
// 引数を1つとってきて
let arg0 = *args.offset(0) as emacs_value;
// rustで利用する形に変更
let r_i = (* env).extract_integer.unwrap()(env, arg0);
// 計算結果をemacs用に変換
(*env).make_integer.unwrap()(env, fib(r_i))
}
// (buffer-substring-no-properties (point-min) (point-max))
unsafe extern fn f_buffer_substring_from_rust (
env: *mut emacs_env,
_: ptrdiff_t,
_: *mut emacs_value,
_: *mut c_void
) -> emacs_value {
let buffer_substring_no_properties = find_function(env, "buffer-substring-no-properties".to_string());
let point_min = find_function(env, "point-min".to_string());
let point_max = find_function(env, "point-max".to_string());
let args1: *mut emacs_value = [].as_mut_ptr();
let args2: *mut emacs_value = [].as_mut_ptr();
let pmin: emacs_value = (* env).funcall.unwrap()(env, point_min, 0, args1);
let pmax: emacs_value = (* env).funcall.unwrap()(env, point_max, 0, args2);
let args3: *mut emacs_value = [pmin, pmax].as_mut_ptr();
let text: emacs_value = (* env).funcall.unwrap()(env, buffer_substring_no_properties, 2, args3);
let mut buf_size: Box<i64> = Box::new(0);
(* env).copy_string_contents.unwrap()(env, text, null_mut(), &mut *buf_size);
let mut buf: Vec<c_char> = Vec::with_capacity(*buf_size as usize);
let buf_ptr: *mut c_char = buf.as_mut_slice().as_mut_ptr();
(* env).copy_string_contents.unwrap()(env, text, buf_ptr, &mut *buf_size);
let buf_str: &[c_char] = std::slice::from_raw_parts_mut(buf_ptr, *buf_size as usize);
let buf_str_ptr: &CStr = std::ffi::CStr::from_ptr(buf_str.as_ptr());
let buf_string: String = buf_str_ptr.to_string_lossy().into_owned();
let new_buf_string: String = format!("{} from rust", buf_string);
(* env).make_string.unwrap()(env, CString::new(new_buf_string.as_str()).unwrap().as_ptr(), new_buf_string.len() as i64)
}
#[no_mangle]
pub extern fn plugin_is_GPL_compatible() -> libc::c_int { 1 }
#[no_mangle]
pub extern fn emacs_module_init(ert: *mut Struct_emacs_runtime) -> c_int {
let env = get_environment(ert);
// 引数1つ
let defun_fib = make_function(env, 1, 1, Some(f_fib), "fibonacci".to_string(), null_mut());
// (fib-r 10)
bind_function(env, "fib-r".to_string(), defun_fib);
// (buffer-substring-r)
let defun_buffer_substring = make_function(env, 0, 0, Some(f_buffer_substring_from_rust), "".to_string(), null_mut());
bind_function(env, "buffer-substring-r".to_string(), defun_buffer_substring);
// (prove 'rust-fib)
provide(env, "rust-fib".to_string());
0
}
ビルド
.soファイルにしないと読み込まなかったので…
$ cargo build
$ mv target/debug/libemacs_rust_fib.dylib target/debug/rust-fib.so
emacsから利用
rust-fib-test.el
(require 'rust-fib)
(message (format "fib(10): %d" (fib-r 10)))
$ src/emacs -Q -L <cargoのパス>/target/debug -batch -l rust-fib-test.el
fib(10): 55
まとめ
rustのみでemacsのdynamic moduleを利用することができました。
cargo build --release
だと上手くいかなかったので、要調査。