LoginSignup
26

More than 5 years have passed since last update.

emacs25のdynamic moduleをrustで作ろう

Last updated at Posted at 2016-06-06

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だと上手くいかなかったので、要調査。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26