emacs25のdynamic moduleをrustで作ろう

  • 27
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

emacs25からdynamic moduleという機能があるようです。
折角なので、rustを使ってその機能を利用してみようと思います。

参考

Emacsに mrubyを組み込んでみた

用意するもの

  1. rust(1.9.0)
  2. 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だと上手くいかなかったので、要調査。