├── .gitignore ├── .travis.yml ├── README.md ├── emacs_module.nim ├── emacs_module.nimble ├── emacs_module └── helpers.nim ├── extra └── nim-emacs-module.el └── test ├── Makefile ├── modtest.nim ├── nim.cfg ├── return42.nim ├── sample.nim ├── test-modtest.el ├── test-return42.el └── test-sample.el /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache/ 2 | /test/*.so 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # still WIP 2 | 3 | sudo: required 4 | services: 5 | - docker 6 | before_install: 7 | - docker pull yglukhov/devel 8 | script: 9 | - docker run -v "$(pwd):/project" -w /project yglukhov/devel nim --version 10 | - docker run -v "$(pwd):/project" -w /project yglukhov/devel nimble tests 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What is this? 2 | This is a wrapper library to use Emacs Dynamic Module feature from 3 | Nim language. 4 | 5 | Note that the Emacs Dynamic Module feature is supported from Emacs 6 | 25.1 onwards. 7 | 8 | ## Note 9 | I'm either Nim and C language's newbie, so probably I'm doing 10 | something wrong... So beware. (PRs are welcome!) 11 | 12 | ## Requirements 13 | 14 | - Emacs with version 25.1 or higher compiled with the `--with-modules` 15 | configure option. 16 | 17 | ## Usage Example 18 | 19 | 1. Clone this repo. 20 | 2. `cd test` 21 | 3. `make sample` 22 | - If the above `make` step fails, set `EMACS_MODULE_DIR` to the 23 | directory containing the `emacs-module.h` header file. Example: 24 | `make sample EMACS_MODULE_DIR=/dir/containing/emacs-module.h/`. 25 | 26 | ### Output 27 | 28 | ``` 29 | emacs --batch -L . -l test.el -f ert-run-tests-batch-and-exit 30 | Running 6 tests (2018-06-21 15:27:04-0400, selector ‘t’) 31 | passed 1/6 sample-mod-test-non-local-exit-signal-test (0.055210 sec) 32 | passed 2/6 sample-mod-test-non-local-exit-throw-test (0.000234 sec) 33 | passed 3/6 sample-mod-test-return-t (0.000264 sec) 34 | passed 4/6 sample-mod-test-return-uname-cmd (0.000247 sec) 35 | passed 5/6 sample-mod-test-sum (0.000267 sec) 36 | passed 6/6 sample-mod-test-vector-test (0.001737 sec) 37 | 38 | Ran 6 tests, 6 results as expected (2018-06-21 15:27:04-0400, 0.058906 sec) 39 | ``` 40 | 41 | ## Another Example 42 | 43 | This example shows how simple it is to write a Nim proc with the extra 44 | functionality as that of the `mymod_test` function in the [Emacs 45 | Modules tutorial][diobla]. 46 | 47 | **Spoiler**: Other than few lines of boilerplate code, all you do is: 48 | 49 | ```nim 50 | emacs.defun(return42, 0): 51 | env.make_integer(env, 42) 52 | ``` 53 | 54 | [Full code][return42] 55 | 56 | Assuming that you already are past Steps 1 and 2 above, do: 57 | 58 | ``` 59 | make return42 60 | ``` 61 | 62 | ### Output 63 | 64 | ``` 65 | emacs --batch -L . -l test-return42.el -f ert-run-tests-batch-and-exit 66 | Running 1 tests (2018-06-21 16:48:28-0400, selector ‘t’) 67 | passed 1/1 return42-return42-cmd (0.000421 sec) 68 | 69 | Ran 1 tests, 1 results as expected (2018-06-21 16:48:28-0400, 0.000766 sec) 70 | ``` 71 | 72 | ## Other References 73 | - [Introduction to Emacs modules][diobla] 74 | - [emacs-mruby-test](https://github.com/syohex/emacs-mruby-test) 75 | - M-x view-emacs-news and then look at `Emacs can now load shared/dynamic libraries (modules).` section 76 | - modules directory of Emacs repository 77 | - [Go + Emacs Modules](https://mrosset.github.io/emacs-module/) 78 | - [GPL Compatible Licenses](https://www.gnu.org/licenses/license-list.html#GPLCompatibleLicenses) 79 | - [Emacs Modules Documentation](http://phst.github.io/emacs-modules.html) 80 | 81 | 82 | [diobla]: http://diobla.info/blog-archive/modules-tut.html 83 | [return42]: test/return42.nim 84 | -------------------------------------------------------------------------------- /emacs_module.nim: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 by Yuta Yamada 2 | 3 | # This program is a wrapper for emacs-module.h to use Nim's functions 4 | # from Emacs using Emacs' dynamic module feature, which is supported 5 | # from Emacs 25.1. 6 | 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | # Comment from Emacs' NEWS 19 | # 20 | # A dynamic Emacs module is a shared library that provides additional 21 | # functionality for use in Emacs Lisp programs, just like a package 22 | # written in Emacs Lisp would. The functions `load', `require', 23 | # `load-file', etc. were extended to load such modules, as they do with 24 | # Emacs Lisp packages. The new variable `module-file-suffix' holds the 25 | # system-dependent value of the file-name extension (`.so' on Posix 26 | # hosts) of the module files. 27 | 28 | # If a module needs to call Emacs functions, it should do so through the 29 | # API defined and documented in the header file `emacs-module.h'. Note 30 | # that any module that provides Lisp-callable functions will have to use 31 | # Emacs functions such as `fset' and `funcall', in order to register its 32 | # functions with the Emacs Lisp interpreter. 33 | 34 | # Modules can create `user-ptr' Lisp objects that embed pointers to C 35 | # struct's defined by the module. This is useful for keeping around 36 | # complex data structures created by a module, to be passed back to the 37 | # module's functions. User-ptr objects can also have associated 38 | # "finalizers" -- functions to be run when the object is GC'ed; this is 39 | # useful for freeing any resources allocated for the underlying data 40 | # structure, such as memory, open file descriptors, etc. A new 41 | # predicate `user-ptrp' returns non-nil if its argument is a `user-ptr' 42 | # object. 43 | 44 | # A module should export a C-callable function named 45 | # `emacs_module_init', which Emacs will call as part of the call to 46 | # `load' or `require' which loads the module. It should also export a 47 | # symbol named `plugin_is_GPL_compatible' to indicate that its code is 48 | # released under the GPL or compatible license; Emacs will refuse to 49 | # load modules that don't export such a symbol. 50 | 51 | # Loadable modules in Emacs are an experimental feature, and subject to 52 | # change in future releases. For that reason, their support is disabled 53 | # by default, and must be enabled by using the `--with-modules' option 54 | # at configure time. 55 | 56 | import emacs_module/helpers 57 | export helpers 58 | 59 | type 60 | intmax_t* {.importc: "intmax_t", header: "".} = clonglong 61 | ptrdiff_t* {.importc: "ptrdiff_t", header: "".} = int 62 | 63 | type 64 | emacs_runtime_private {.importc: "struct emacs_runtime_private", 65 | header: "".} = object 66 | 67 | emacs_env_private {.importc: "struct emacs_env_private", 68 | header: "".} = object 69 | 70 | type 71 | ## Function prototype for the module init function. 72 | emacs_init_function* = proc (ert: ptr emacs_runtime): cint {.cdecl.} 73 | 74 | ## Function prototype for the module Lisp functions. 75 | emacs_subr* = proc(env: ptr emacs_env, 76 | nargs: ptrdiff_t, 77 | args: ptr emacs_value, 78 | data: pointer): emacs_value {.cdecl.} 79 | emacs_runtime* {.importc: "struct emacs_runtime", 80 | header: "".} = object ## \ 81 | ## Struct passed to a module init function (emacs_module_init). 82 | size: ptrdiff_t ## Structure size (for version checking). 83 | private_members: ptr emacs_runtime_private ## \ 84 | ## Private data; users should not touch this. 85 | get_environment: proc(ert: ptr emacs_runtime): ptr emacs_env {.cdecl.} 86 | 87 | emacs_value* {.importc: "emacs_value", 88 | header: "".} = pointer 89 | 90 | emacs_funcall_exit* = enum 91 | emacs_funcall_exit_return = 0, # Function has returned normally. 92 | emacs_funcall_exit_signal = 1, # Function has signaled an error using `signal'. 93 | emacs_funcall_exit_throw = 2 # Function has exit using `throw'. 94 | 95 | emacs_env* {.importc: "emacs_env", 96 | header: "".} = object 97 | size: ptrdiff_t 98 | private_members*: ptr emacs_env_private 99 | make_global_ref*: proc (env: ptr emacs_env; 100 | any_reference: emacs_value): emacs_value {.cdecl.} 101 | free_global_ref*: proc (env: ptr emacs_env; 102 | global_reference: emacs_value) {.cdecl.} 103 | non_local_exit_check*: 104 | proc (env: ptr emacs_env): emacs_funcall_exit {.cdecl.} 105 | non_local_exit_clear*: proc (env: ptr emacs_env) {.cdecl.} 106 | non_local_exit_get*: proc (env: ptr emacs_env; 107 | non_local_exit_symbol_out: ptr emacs_value; 108 | non_local_exit_data_out: ptr emacs_value 109 | ): emacs_funcall_exit {.cdecl.} 110 | non_local_exit_signal*: proc (env: ptr emacs_env; 111 | non_local_exit_symbol: emacs_value; 112 | non_local_exit_data: emacs_value) {.cdecl.} 113 | non_local_exit_throw*: proc (env: ptr emacs_env; tag: emacs_value; 114 | value: emacs_value) {.cdecl.} 115 | 116 | # Function registration 117 | make_function*: proc (env: ptr emacs_env, 118 | min_arity: ptrdiff_t; 119 | max_arity: ptrdiff_t; 120 | function: proc (env: ptr emacs_env; 121 | nargs: ptrdiff_t; 122 | args: ptr emacs_value; 123 | _: pointer): emacs_value {.cdecl.}, 124 | documentation: cstring; 125 | data: pointer): emacs_value {.cdecl.} 126 | funcall*: proc (env: ptr emacs_env; function: emacs_value; nargs: ptrdiff_t; 127 | args: ptr emacs_value): emacs_value {.cdecl.} 128 | intern*: proc (env: ptr emacs_env; 129 | symbol_name: cstring): emacs_value {.cdecl.} 130 | 131 | # Type Conversion 132 | type_of*: proc (env: ptr emacs_env; 133 | value: emacs_value): emacs_value {.cdecl.} 134 | is_not_nil*: proc (env: ptr emacs_env; 135 | value: emacs_value): bool {.cdecl.} 136 | eq*: proc (env: ptr emacs_env; 137 | a: emacs_value; b: emacs_value): bool {.cdecl.} 138 | extract_integer*: proc (env: ptr emacs_env; 139 | value: emacs_value): intmax_t {.cdecl.} 140 | make_integer*: proc (env: ptr emacs_env; 141 | value: intmax_t): emacs_value {.cdecl.} 142 | extract_float*: proc (env: ptr emacs_env; 143 | value: emacs_value): cdouble {.cdecl.} 144 | make_float*: proc (env: ptr emacs_env; 145 | value: cdouble): emacs_value {.cdecl.} 146 | 147 | # String manipulation 148 | copy_string_contents*: proc (env: ptr emacs_env; 149 | value: emacs_value; 150 | buffer: cstring; 151 | size_inout: ptr ptrdiff_t): bool {.cdecl.} 152 | 153 | # Create a Lisp string from a utf8 encoded string. 154 | make_string*: proc (env: ptr emacs_env; 155 | contents: cstring; 156 | length: ptrdiff_t): emacs_value {.cdecl.} 157 | 158 | # Embedded pointer type. 159 | make_user_ptr*: proc (env: ptr emacs_env; 160 | fin: proc (_: pointer) {.cdecl.}, 161 | `ptr`: pointer): emacs_value {.cdecl.} 162 | get_user_ptr*: proc (env: ptr emacs_env; 163 | uptr: emacs_value): pointer {.cdecl.} 164 | set_user_ptr*: proc (env: ptr emacs_env; 165 | uptr: emacs_value; `ptr`: pointer) {.cdecl.} 166 | 167 | set_user_finalizer*: proc (env: ptr emacs_env; uptr: emacs_value; 168 | fin: proc (_: pointer) {.cdecl.}) {.cdecl.} 169 | 170 | # Vector 171 | vec_get*: proc (env: ptr emacs_env; 172 | vec: emacs_value; i: ptrdiff_t): emacs_value {.cdecl.} 173 | vec_set*: proc (env: ptr emacs_env; 174 | vec: emacs_value; i: ptrdiff_t; val: emacs_value) {.cdecl.} 175 | vec_size*: proc (env: ptr emacs_env; vec: emacs_value): ptrdiff_t {.cdecl.} 176 | 177 | # TODO: Below seems to be unused 178 | # proc emacs_module_init*(ert: ptr emacs_runtime): cint 179 | # {.importc: "emacs_module_init", header: "".} ## \ 180 | # ## Every module should define a function as follows. 181 | -------------------------------------------------------------------------------- /emacs_module.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "Yuta Yamada" 5 | description = "Call Nim procs from Emacs 25+ using its Dynamic Modules feature" 6 | license = "GPLv3" 7 | skipDirs = @["test"] 8 | 9 | # Dependencies 10 | 11 | requires "nim >= 0.14.0" 12 | -------------------------------------------------------------------------------- /emacs_module/helpers.nim: -------------------------------------------------------------------------------- 1 | import strutils 2 | 3 | 4 | type Emacs* = object 5 | functions*: string 6 | libName*: string 7 | 8 | 9 | proc pushFunction*(self: var Emacs, fn: string, max_args: int) = 10 | ## Push function name ``fn`` to ``functions`` object. 11 | ## This variable is used later by ``provide`` proc. 12 | let 13 | emacs_func = replace(self.libName & "-" & fn, "_", "-") 14 | nim_func = "nimEmacs_" & self.libName & "_" & fn 15 | 16 | self.functions.add( 17 | format("""DEFUN ("$1", $2, $3, $4, NULL, NULL); """, 18 | emacs_func, nim_func, max_args, max_args) 19 | ) 20 | 21 | 22 | template defun*(self: Emacs; fsym: untyped; max_args: int; body: untyped) {.dirty.} = ## \ 23 | ## emacs_func(env: ptr emacs_env, nargs: ptrdiff_t, 24 | ## args: ptr array[0..max_args, emacs_value], data: pointer): 25 | ## emacs_value {.exportc.} 26 | ## The `fsym` is registered as the name in emacs and also 27 | ## be registered in Nim with nimEmacs prefix. 28 | ## If you include "_" in the function name, it will be converted "-" 29 | ## in Emacs. 30 | static: 31 | self.pushFunction(astToStr(fsym), max_args) 32 | 33 | proc `fsym`*(env: ptr emacs_env, nargs: ptrdiff_t, 34 | args: ptr array[0..max_args, emacs_value], 35 | data: pointer): emacs_value {.exportc,extern: "nimEmacs_" & self.libName & "_$1".} = 36 | body 37 | 38 | 39 | proc provideString* (self: Emacs): string = 40 | format(""" 41 | /* Lisp utilities for easier readability (simple wrappers). */ 42 | 43 | /* Provide FEATURE to Emacs. */ 44 | static void 45 | provide (emacs_env *env, const char *feature) 46 | { 47 | emacs_value Qfeat = env->intern (env, feature); 48 | emacs_value Qprovide = env->intern (env, "provide"); 49 | emacs_value args[] = { Qfeat }; 50 | 51 | env->funcall (env, Qprovide, 1, args); 52 | } 53 | 54 | /* Bind NAME to FUN. */ 55 | static void 56 | bind_function (emacs_env *env, const char *name, emacs_value Sfun) 57 | { 58 | emacs_value Qfset = env->intern (env, "fset"); 59 | emacs_value Qsym = env->intern (env, name); 60 | emacs_value args[] = { Qsym, Sfun }; 61 | 62 | env->funcall (env, Qfset, 2, args); 63 | } 64 | 65 | /* Module init function. */ 66 | int 67 | emacs_module_init (struct emacs_runtime *ert) 68 | { 69 | emacs_env *env = ert->get_environment (ert); 70 | NimMain(); // <- Nim executes this in `main` function 71 | #define DEFUN(lsym, csym, amin, amax, doc, data) \ 72 | bind_function (env, lsym, \ 73 | env->make_function (env, amin, amax, csym, doc, data)) 74 | $1 75 | 76 | #undef DEFUN 77 | 78 | provide (env, "$2"); 79 | return 0; 80 | 81 | } 82 | """, self.functions, self.libName) 83 | 84 | 85 | template provide*(self: Emacs) {.dirty.} = 86 | static: 87 | const temp = self.provideString() 88 | {.emit: temp.} 89 | 90 | 91 | template init*(sym: untyped) {.dirty.} = 92 | from os import splitFile 93 | 94 | static: 95 | var `sym` = Emacs() 96 | let info = instantiationInfo() 97 | sym.functions = "" 98 | # If the file name is foo.nim, let the libary name to foo. 99 | sym.libName = splitFile(info.filename).name 100 | -------------------------------------------------------------------------------- /extra/nim-emacs-module.el: -------------------------------------------------------------------------------- 1 | ;;; nim-emacs-module.el --- Make Emacs functions by Nim -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2016 Yuta Yamada 4 | 5 | ;; Author: Yuta Yamada 6 | ;; URL: https://github.com/yuutayamada/nim-emacs-module 7 | ;; Version: 0.1.0 8 | ;; Package-Requires: ((emacs "25.0")) 9 | ;; Keywords: convenience, Nim 10 | 11 | ;;; License: 12 | ;; This program is free software: you can redistribute it and/or modify 13 | ;; it under the terms of the GNU General Public License as published by 14 | ;; the Free Software Foundation, either version 3 of the License, or 15 | ;; (at your option) any later version. 16 | ;; 17 | ;; This program is distributed in the hope that it will be useful, 18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | ;; GNU General Public License for more details. 21 | ;; 22 | ;; You should have received a copy of the GNU General Public License 23 | ;; along with this program. If not, see . 24 | 25 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 26 | ;;; Commentary: 27 | ;; 28 | ;; Requirements: 29 | ;; 30 | ;; This package requires Emacs 25 or higher version because of using 31 | ;; Emacs’ dynamic module feature and you may need to compile your Emacs 32 | ;; with --with-modules option. Also you need Nim compiler to compile 33 | ;; nim files. 34 | ;; 35 | ;; Usage: 36 | ;; 37 | ;; 1. Do M-x ‘nim-emacs-module-init’. 38 | ;; It will create nim-emacs-module directory in your 39 | ;; ‘user-emacs-directory’ and put a nim.cfg file (for Nim’s 40 | ;; configuration file) 41 | ;; 2. Go to the nim-emacs-module directory. 42 | ;; 3. Make a nim file in the nim-emacs-module directory. 43 | ;; 4. Do M-x ‘nim-emacs-module-insert-template’ 44 | ;; 5. Follow the inserted instruction or see the examples of test directory. 45 | ;; 6. Have fun :) 46 | ;; 47 | ;; Note that this package is still working in progress and I might change 48 | ;; some details. 49 | ;; 50 | ;; Other References: 51 | ;; - [Introduction to Emacs modules](http://diobla.info/blog-archive/modules-tut.html) 52 | ;; - [emacs-mruby-test](https://github.com/syohex/emacs-mruby-test) 53 | ;; - M-x view-emacs-news and then look at `Emacs can now load 54 | ;; shared/dynamic libraries (modules).` section 55 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 56 | ;;; Code: 57 | 58 | (require 'subr-x) 59 | (require 'let-alist) 60 | 61 | (defvar nim-emacs-module-template 62 | "# `plugin_is_GPL_compatible` indicates that its code is 63 | # released under the GPL or compatible license; Emacs will refuse to 64 | # load modules that don't export such a symbol. 65 | {.emit:\"int plugin_is_GPL_compatible;\".} 66 | 67 | 68 | import emacs_module # Primitive wrapper for emacs_module.h 69 | 70 | 71 | # You can access following types: 72 | # 73 | # env: ptr emacs_env 74 | # nargs: ptrdiff_t 75 | # args: ptr array[0..max_args, emacs_value] 76 | # data: pointer 77 | 78 | init(emacs) 79 | 80 | %s 81 | 82 | # Provide functions registered by above from here. 83 | emacs.provide()%s" 84 | "Template string.") 85 | 86 | (defvar nim-emacs-module-template-example 87 | '((example . " 88 | # Example(Create a file named `mod_test.nim`): 89 | emacs.defun(return_t, 1): 90 | env.intern(env, \"t\".cstring)") 91 | (test . " 92 | 93 | # How to test the example's code: 94 | # 95 | # $ emacs -Q -L . 96 | # 97 | # and then in *scratch* buffer, evaluate those lines: 98 | # 99 | # The file name ‘mod-test‘ is added as prefix name of each functions and 100 | # its package name. 101 | # (require '%s) 102 | # (mod-test-return-t)")) 103 | "Additional template.") 104 | 105 | (defvar nim-emacs-module-dir 106 | (when-let ((pkg (locate-library "nim-emacs-module"))) 107 | (file-name-directory pkg)) 108 | "Directly where the nim-emacs-module.nim is placed.") 109 | 110 | (defvar nim-emacs-module-module-h-dir 111 | (let ((dir (file-name-directory (executable-find "emacs")))) 112 | (if (file-exists-p (format "%semacs-module.h" dir)) 113 | dir 114 | nil)) 115 | "Directly of emacs-module.h.") 116 | 117 | 118 | (defvar nim-emacs-module-dylib-dir 119 | (format "%snim-emacs-module/" user-emacs-directory) 120 | "The place where the .so files are placed.") 121 | 122 | (defvar nim-emacs-module-cflags 123 | "--passC:-std=gnu99") 124 | 125 | ;;;###autoload 126 | (defun nim-emacs-module-insert-template () 127 | "Insert template for nim-emacs-module." 128 | (interactive) 129 | (let-alist nim-emacs-module-template-example 130 | (let ((example .example) 131 | (test (if (string< "" .test) 132 | (format .test (file-name-base)) 133 | ""))) 134 | (insert (format nim-emacs-module-template 135 | example 136 | (file-name-base) 137 | test))))) 138 | 139 | ;;;###autoload 140 | (defun nim-emacs-module-init () 141 | "Prepare things for nim-emacs-module." 142 | (interactive) 143 | (nim-emacs-module--make-dir nim-emacs-module-dylib-dir) 144 | (let ((file (format "%snim.cfg" nim-emacs-module-dylib-dir))) 145 | (unless (file-exists-p file) 146 | (if (not (file-directory-p nim-emacs-module-dir)) 147 | (error "Please set ‘nim-emacs-module-dir’") 148 | (save-current-buffer 149 | (with-temp-file file 150 | (insert (format "path: \"%s\"" nim-emacs-module-dir)))))))) 151 | 152 | (defun nim-emacs-module--assert (file) 153 | "Check some requirements, according to FILE." 154 | (cond 155 | ((not (file-exists-p file)) 156 | (error "Error: %s doesn't exist" file)) 157 | ((not (version< "25.0.0.0" emacs-version)) 158 | (error "Error: need Emacs version higher than 25")) 159 | ((not (derived-mode-p 'nim-mode)) 160 | (yes-or-no-p "major-mode isn’t nim-mode. Are you really sure?")) 161 | (t t))) 162 | 163 | (defun nim-emacs-module--make-dir (directory) 164 | "Make DIRECTORY." 165 | (unless (file-directory-p directory) 166 | (make-directory directory))) 167 | 168 | (defun nim-emacs-module--compile (file) 169 | "Compile a nim FILE to a .so/.dylib/.dll extension file." 170 | (when (nim-emacs-module--assert file) 171 | (let ((cmd (nim-emacs-module--get-command file))) 172 | (async-shell-command 173 | cmd (get-buffer-create "*nim-emacs-module-compile*"))))) 174 | 175 | (defun nim-emacs-module--get-command (file) 176 | "Return command using FILE." 177 | (let ((emacs-module-h (format "--passC:-I%s" nim-emacs-module-module-h-dir)) 178 | (cflags nim-emacs-module-cflags) 179 | (base (file-name-base file))) 180 | (format "nim c --out:%s.%s --app:lib %s %s %s" 181 | base (nim-emacs-module--get-extension) 182 | cflags emacs-module-h file))) 183 | 184 | (defun nim-emacs-module--get-extension () 185 | "Return file extension." 186 | (cl-case system-type 187 | ;; I’m only testing on Linux, so please let me know 188 | ;; if it’s something wrong... (is the cygwin right place?) 189 | ((ms-dos windows-nt cygwin) "dll") 190 | (darwin "dylib") 191 | (t "so"))) 192 | 193 | ;;;###autoload 194 | (defun nim-emacs-module-compile () 195 | "Compile current Nim file to a dylib file that can Emacs load." 196 | (interactive) 197 | (nim-emacs-module--make-dir nim-emacs-module-dylib-dir) 198 | (let ((default-directory nim-emacs-module-dylib-dir)) 199 | (nim-emacs-module--compile buffer-file-name))) 200 | 201 | ;; Work with nim-mode 202 | 203 | (defun nim-emacs-module-file-p () 204 | "Return t if current file is inside ‘nim-emacs-module-dylib-dir’." 205 | (string-match nim-emacs-module-dylib-dir buffer-file-name)) 206 | 207 | ;;;###autoload 208 | (defun nim-compile-or-module-compile (&rest _r) 209 | "Work in progress." 210 | (interactive) 211 | (call-interactively 212 | (if (nim-emacs-module-file-p) 213 | 'nim-emacs-module-compile 214 | 'nim-compile))) 215 | 216 | ;;;###autoload 217 | (defun nim-emacs-module-setup-nim-mode () 218 | "Setup for nim-mode." 219 | (remove-hook 'nim-mode-hook 'nim-emacs-module-setup-nim-mode) 220 | (with-no-warnings 221 | (define-key nim-mode-map [remap nim-compile] 'nim-compile-or-module-compile))) 222 | 223 | ;;;###autoload 224 | (with-eval-after-load "nim-mode" 225 | (add-hook 'nim-mode-hook 'nim-emacs-module-setup-nim-mode)) 226 | 227 | (provide 'nim-emacs-module) 228 | 229 | ;; Local Variables: 230 | ;; coding: utf-8 231 | ;; mode: emacs-lisp 232 | ;; End: 233 | 234 | ;;; nim-emacs-module.el ends here 235 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | # !! Set EMACS_MODULE_DIR to the directory containing the emacs-module.h !! 2 | EMACS_MODULE_DIR ?= $(shell dirname `which emacs`) 3 | EMACS ?= emacs 4 | 5 | NIM_CFLAGS = --passC:-I$(EMACS_MODULE_DIR) --passC:-std=gnu99 6 | 7 | .PHONY: return42 test_return42 sample test_sample modtest test_modtest clean test all 8 | 9 | 10 | ## return42 11 | return42: clean return42.so test_return42 12 | 13 | return42.so: return42.nim 14 | nim c --out:return42.so --app:lib $(NIM_CFLAGS) $< 15 | 16 | test_return42: 17 | $(EMACS) --batch -L . $(LOADPATH) -l test-return42.el -f ert-run-tests-batch-and-exit 18 | 19 | 20 | ## sample 21 | sample: clean sample.so test_sample 22 | 23 | sample.so: sample.nim 24 | nim c --out:sample.so --app:lib $(NIM_CFLAGS) $< 25 | 26 | test_sample: 27 | $(EMACS) --batch -L . $(LOADPATH) -l test-sample.el -f ert-run-tests-batch-and-exit 28 | 29 | 30 | ## modtest 31 | modtest: clean modtest.so test_modtest 32 | 33 | modtest.so: modtest.nim 34 | nim c --out:modtest.so --app:lib $(NIM_CFLAGS) $< 35 | 36 | test_modtest: 37 | $(EMACS) --batch -L . $(LOADPATH) -l test-modtest.el -f ert-run-tests-batch-and-exit 38 | 39 | 40 | clean: 41 | rm -fr nimcache *.so 42 | 43 | test: test_return42 test_sample test_modtest 44 | 45 | all: return42 sample modtest 46 | -------------------------------------------------------------------------------- /test/modtest.nim: -------------------------------------------------------------------------------- 1 | #[ 2 | 3 | License: 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | 18 | ]# 19 | 20 | # Official test file for Emacs Modules: 21 | # http://git.savannah.gnu.org/cgit/emacs.git/tree/test/data/emacs-module/mod-test.c 22 | 23 | # `plugin_is_GPL_compatible` indicates that its code is 24 | # released under the GPL or compatible license; Emacs will refuse to 25 | # load modules that don't export such a symbol. 26 | {.emit:"int plugin_is_GPL_compatible;".} 27 | 28 | import emacs_module # primitive wrapper for emacs_module.h 29 | 30 | init(emacs) 31 | 32 | #[ 33 | struct emacs_env_26 34 | { 35 | 36 | /* Structure size (for version checking). */ 37 | ptrdiff_t size; 38 | 39 | /* Private data; users should not touch this. */ 40 | struct emacs_env_private *private_members; 41 | ]# 42 | 43 | #[ 44 | /* Memory management. */ 45 | 46 | emacs_value (*make_global_ref) (emacs_env *env, 47 | emacs_value any_reference) 48 | EMACS_ATTRIBUTE_NONNULL(1); 49 | 50 | void (*free_global_ref) (emacs_env *env, 51 | emacs_value global_reference) 52 | EMACS_ATTRIBUTE_NONNULL(1); 53 | ]# 54 | 55 | emacs.defun(globref_make, 0): 56 | ## Make a big string and make it global. 57 | var str: string = "" 58 | for i in 0 ..< (26 * 100): 59 | str.add(char('a'.ord + (i mod 26))) 60 | let lispStr = env.make_string(env, addr str[0], str.len) 61 | return env.make_global_ref(env, lispStr) 62 | 63 | #[ 64 | /* Non-local exit handling. */ 65 | 66 | #[ 67 | Exit status enum: 68 | 69 | /* Function has returned normally. */ 70 | emacs_funcall_exit_return = 0, 71 | 72 | /* Function has signaled an error using `signal'. */ 73 | emacs_funcall_exit_signal = 1, 74 | 75 | /* Function has exit using `throw'. */ 76 | emacs_funcall_exit_throw = 2, 77 | ]# 78 | 79 | enum emacs_funcall_exit (*non_local_exit_check) (emacs_env *env) 80 | EMACS_ATTRIBUTE_NONNULL(1); 81 | 82 | void (*non_local_exit_clear) (emacs_env *env) 83 | EMACS_ATTRIBUTE_NONNULL(1); 84 | 85 | enum emacs_funcall_exit (*non_local_exit_get) 86 | (emacs_env *env, 87 | emacs_value *non_local_exit_symbol_out, 88 | emacs_value *non_local_exit_data_out) 89 | EMACS_ATTRIBUTE_NONNULL(1, 2, 3); 90 | 91 | void (*non_local_exit_signal) (emacs_env *env, 92 | emacs_value non_local_exit_symbol, 93 | emacs_value non_local_exit_data) 94 | EMACS_ATTRIBUTE_NONNULL(1); 95 | 96 | void (*non_local_exit_throw) (emacs_env *env, 97 | emacs_value tag, 98 | emacs_value value) 99 | EMACS_ATTRIBUTE_NONNULL(1); 100 | ]# 101 | 102 | # non_local_exit_check, non_local_exit_signal 103 | emacs.defun(signal, 0): 104 | assert(env.non_local_exit_check(env) == emacs_funcall_exit_return) 105 | env.non_local_exit_signal(env, env.intern(env, "error"), 106 | env.make_integer(env, 100)) 107 | 108 | # non_local_exit_check, non_local_exit_throw 109 | emacs.defun(throw, 0): 110 | assert(env.non_local_exit_check(env) == emacs_funcall_exit_return) 111 | env.non_local_exit_throw(env, env.intern(env, "tag"), 112 | env.make_integer(env, 42)) 113 | result = env.intern(env, "nil") 114 | 115 | # non_local_exit_get, non_local_exit_clear 116 | emacs.defun(non_local_exit_funcall, 1): 117 | ## Call argument function (which takes 0 arguments), catch all 118 | ## non-local exists and return either normal result or a list 119 | ## describing the non-local exit. 120 | let elispFuncallRet = env.funcall(env, args[0], 0, nil) 121 | var 122 | non_local_exit_symbol, non_local_exit_data: emacs_value 123 | let exitCode: emacs_funcall_exit = 124 | env.non_local_exit_get(env, 125 | addr non_local_exit_symbol, 126 | addr non_local_exit_data) 127 | 128 | case exitCode 129 | of emacs_funcall_exit_return: 130 | return elispFuncallRet 131 | of emacs_funcall_exit_signal: 132 | env.non_local_exit_clear(env) 133 | let 134 | Flist = env.intern(env, "list") 135 | var 136 | listArgs: array[3, emacs_value] = [env.intern(env, "signal"), 137 | non_local_exit_symbol, 138 | non_local_exit_data] 139 | return env.funcall(env, Flist, 3, addr listArgs[0]) 140 | of emacs_funcall_exit_throw: 141 | env.non_local_exit_clear(env) 142 | let 143 | Flist = env.intern(env, "list") 144 | var 145 | listArgs: array[3, emacs_value] = [env.intern(env, "throw"), 146 | non_local_exit_symbol, 147 | non_local_exit_data] 148 | return env.funcall(env, Flist, 3, addr listArgs[0]) 149 | 150 | #[ 151 | /* Function registration. */ 152 | 153 | emacs_value (*make_function) (emacs_env *env, 154 | ptrdiff_t min_arity, 155 | ptrdiff_t max_arity, 156 | emacs_value (*function) (emacs_env *env, 157 | ptrdiff_t nargs, 158 | emacs_value args[], 159 | void *) 160 | EMACS_NOEXCEPT 161 | EMACS_ATTRIBUTE_NONNULL(1), 162 | const char *documentation, 163 | void *data) 164 | EMACS_ATTRIBUTE_NONNULL(1, 4); 165 | 166 | emacs_value (*funcall) (emacs_env *env, 167 | emacs_value function, 168 | ptrdiff_t nargs, 169 | emacs_value args[]) 170 | EMACS_ATTRIBUTE_NONNULL(1); 171 | 172 | emacs_value (*intern) (emacs_env *env, 173 | const char *symbol_name) 174 | EMACS_ATTRIBUTE_NONNULL(1, 2); 175 | ]# 176 | 177 | emacs.defun(make_string, 2): 178 | ## Returns string created by Emacs-Lisp ``make-string``. 179 | let 180 | fSymbol = env.intern(env, "make-string") # Get 'make-string 181 | return env.funcall(env, fSymbol, nargs, addr args[0]) # Return the (funcall make-string ..) returned elisp string 182 | 183 | emacs.defun(return_t, 1): 184 | ## Returns ``t``, always. 185 | env.intern(env, "t") 186 | 187 | #[ 188 | /* Type conversion. */ 189 | 190 | emacs_value (*type_of) (emacs_env *env, 191 | emacs_value value) 192 | EMACS_ATTRIBUTE_NONNULL(1); 193 | 194 | bool (*is_not_nil) (emacs_env *env, emacs_value value) 195 | EMACS_ATTRIBUTE_NONNULL(1); 196 | 197 | bool (*eq) (emacs_env *env, emacs_value a, emacs_value b) 198 | EMACS_ATTRIBUTE_NONNULL(1); 199 | 200 | intmax_t (*extract_integer) (emacs_env *env, emacs_value value) 201 | EMACS_ATTRIBUTE_NONNULL(1); 202 | 203 | emacs_value (*make_integer) (emacs_env *env, intmax_t value) 204 | EMACS_ATTRIBUTE_NONNULL(1); 205 | 206 | double (*extract_float) (emacs_env *env, emacs_value value) 207 | EMACS_ATTRIBUTE_NONNULL(1); 208 | 209 | emacs_value (*make_float) (emacs_env *env, double value) 210 | EMACS_ATTRIBUTE_NONNULL(1); 211 | ]# 212 | 213 | emacs.defun(get_type, 1): 214 | ## Returns the Emacs-Lisp symbol of the argument type. 215 | ## See http://git.savannah.gnu.org/cgit/emacs.git/tree/src/data.c?id=5583e6460c38c5d613e732934b066421349a5259#n204. 216 | env.type_of(env, args[0]) 217 | 218 | emacs.defun(is_true, 1): 219 | ## Returns ``t`` if argument is non-nil, else returns ``nil``. 220 | if env.is_not_nil(env, args[0]): 221 | env.intern(env, "t") 222 | else: 223 | env.intern(env, "nil") 224 | 225 | emacs.defun(eq, 2): 226 | ## Returns ``t`` if both arguments are the same Lisp object, else returns ``nil``. 227 | ## Note that this returns the value of Emacs-Lisp ``eq``, not ``equal``. 228 | if env.eq(env, args[0], args[1]): 229 | env.intern(env, "t") 230 | else: 231 | env.intern(env, "nil") 232 | 233 | emacs.defun(sum, 2): 234 | ## Returns the sum of two integers. 235 | # assert(nargs == 2) 236 | # The above assert statement is never reached! Emacs throws the 237 | # "wrong-number-of-arguments" error signal before the execution 238 | # reaches this assert statement. 239 | let 240 | a = env.extract_integer(env, args[0]) 241 | b = env.extract_integer(env, args[1]) 242 | env.make_integer(env, a + b) 243 | 244 | emacs.defun(sum_float, 2): 245 | ## Returns the sum of two floats. 246 | let 247 | a = env.extract_float(env, args[0]) 248 | b = env.extract_float(env, args[1]) 249 | env.make_float(env, a + b) 250 | 251 | #[ 252 | /* Create a Lisp string from a utf8 encoded string. */ 253 | emacs_value (*make_string) (emacs_env *env, 254 | const char *contents, ptrdiff_t length) 255 | EMACS_ATTRIBUTE_NONNULL(1, 2); 256 | ]# 257 | 258 | # make_string 259 | emacs.defun(lazy, 0): 260 | ## Returns a string constant. 261 | var str = "The quick brown fox jumped over the lazy dog." 262 | return env.make_string(env, addr str[0], str.len) 263 | 264 | #[ 265 | /* Copy the content of the Lisp string VALUE to BUFFER as an utf8 266 | null-terminated string. 267 | 268 | SIZE must point to the total size of the buffer. If BUFFER is 269 | NULL or if SIZE is not big enough, write the required buffer size 270 | to SIZE and return true. 271 | 272 | Note that SIZE must include the last null byte (e.g. "abc" needs 273 | a buffer of size 4). 274 | 275 | Return true if the string was successfully copied. */ 276 | 277 | bool (*copy_string_contents) (emacs_env *env, 278 | emacs_value value, 279 | char *buffer, 280 | ptrdiff_t *size_inout) 281 | EMACS_ATTRIBUTE_NONNULL(1, 4); 282 | ]# 283 | 284 | # copy_string_contents, make_string 285 | emacs.defun(hello, 1): 286 | ## Returns "Hello " prefixed to the passed string argument. 287 | var l: ptrdiff_t 288 | if (env.copy_string_contents(env, args[0], nil, addr l)): # Get the length of the elisp string args[0] (it's num chars + 1). 289 | var name = newString(l-1) # So the actual string length is l-1. Allocate that much space for the name string in Nim. 290 | if (env.copy_string_contents(env, args[0], addr name[0], addr l)): # *Now* copy the elisp string args[0] to Nim string name. 291 | var res = "Hello " & name # Create a new Nim string res using the name string. 292 | return env.make_string(env, addr res[0], res.len) # Convert the Nim string res to elisp string before returning. 293 | 294 | from osproc import execCmdEx 295 | from strutils import strip 296 | emacs.defun(uname, 1): 297 | ## Returns the output of the ``uname`` command run the passed string argument. 298 | var l: ptrdiff_t 299 | if (env.copy_string_contents(env, args[0], nil, addr l)): 300 | var unameArg = newString(l-1) 301 | if (env.copy_string_contents(env, args[0], addr unameArg[0], addr l)): 302 | var (res, _) = execCmdEx("uname " & unameArg) 303 | res = res.strip() 304 | result = env.make_string(env, addr res[0], res.len) 305 | 306 | # /* Return a copy of the argument string where every 'a' is replaced 307 | # with 'b'. */ 308 | # static emacs_value 309 | # Fmod_test_string_a_to_b (emacs_env *env, ptrdiff_t nargs, emacs_value args[], 310 | # void *data) 311 | # { 312 | # emacs_value lisp_str = args[0]; 313 | # ptrdiff_t size = 0; 314 | # char * buf = NULL; 315 | 316 | # env->copy_string_contents (env, lisp_str, buf, &size); 317 | # buf = malloc (size); 318 | # env->copy_string_contents (env, lisp_str, buf, &size); 319 | 320 | # for (ptrdiff_t i = 0; i + 1 < size; i++) 321 | # if (buf[i] == 'a') 322 | # buf[i] = 'b'; 323 | 324 | # return env->make_string (env, buf, size - 1); 325 | # } 326 | 327 | 328 | #[ 329 | /* Embedded pointer type. */ 330 | emacs_value (*make_user_ptr) (emacs_env *env, 331 | void (*fin) (void *) EMACS_NOEXCEPT, 332 | void *ptr) 333 | EMACS_ATTRIBUTE_NONNULL(1); 334 | 335 | void *(*get_user_ptr) (emacs_env *env, emacs_value uptr) 336 | EMACS_ATTRIBUTE_NONNULL(1); 337 | void (*set_user_ptr) (emacs_env *env, emacs_value uptr, void *ptr) 338 | EMACS_ATTRIBUTE_NONNULL(1); 339 | ]# 340 | 341 | # /* Embedded pointers in lisp objects. */ 342 | 343 | # /* C struct (pointer to) that will be embedded. */ 344 | # struct super_struct 345 | # { 346 | # int amazing_int; 347 | # char large_unused_buffer[512]; 348 | # }; 349 | 350 | # /* Return a new user-pointer to a super_struct, with amazing_int set 351 | # to the passed parameter. */ 352 | # static emacs_value 353 | # Fmod_test_userptr_make (emacs_env *env, ptrdiff_t nargs, emacs_value args[], 354 | # void *data) 355 | # { 356 | # struct super_struct *p = calloc (1, sizeof *p); 357 | # p->amazing_int = env->extract_integer (env, args[0]); 358 | # return env->make_user_ptr (env, free, p); 359 | # } 360 | 361 | # /* Return the amazing_int of a passed 'user-pointer to a super_struct'. */ 362 | # static emacs_value 363 | # Fmod_test_userptr_get (emacs_env *env, ptrdiff_t nargs, emacs_value args[], 364 | # void *data) 365 | # { 366 | # struct super_struct *p = env->get_user_ptr (env, args[0]); 367 | # return env->make_integer (env, p->amazing_int); 368 | # } 369 | 370 | # static emacs_value invalid_stored_value; 371 | 372 | # /* The next two functions perform a possibly-invalid operation: they 373 | # store a value in a static variable and load it. This causes 374 | # undefined behavior if the environment that the value was created 375 | # from is no longer live. The module assertions check for this 376 | # error. */ 377 | 378 | # static emacs_value 379 | # Fmod_test_invalid_store (emacs_env *env, ptrdiff_t nargs, emacs_value *args, 380 | # void *data) 381 | # { 382 | # return invalid_stored_value = env->make_integer (env, 123); 383 | # } 384 | 385 | # static emacs_value 386 | # Fmod_test_invalid_load (emacs_env *env, ptrdiff_t nargs, emacs_value *args, 387 | # void *data) 388 | # { 389 | # return invalid_stored_value; 390 | # } 391 | 392 | # /* An invalid finalizer: Finalizers are run during garbage collection, 393 | # where Lisp code can’t be executed. -module-assertions tests for 394 | # this case. */ 395 | 396 | # static emacs_env *current_env; 397 | 398 | # static void 399 | # invalid_finalizer (void *ptr) 400 | # { 401 | # current_env->intern (current_env, "nil"); 402 | # } 403 | 404 | # static emacs_value 405 | # Fmod_test_invalid_finalizer (emacs_env *env, ptrdiff_t nargs, emacs_value *args, 406 | # void *data) 407 | # { 408 | # current_env = env; 409 | # env->make_user_ptr (env, invalid_finalizer, NULL); 410 | # return env->funcall (env, env->intern (env, "garbage-collect"), 0, NULL); 411 | # } 412 | 413 | #[ 414 | void (*(*get_user_finalizer) (emacs_env *env, emacs_value uptr)) 415 | (void *) EMACS_NOEXCEPT EMACS_ATTRIBUTE_NONNULL(1); 416 | void (*set_user_finalizer) (emacs_env *env, 417 | emacs_value uptr, 418 | void (*fin) (void *) EMACS_NOEXCEPT) 419 | EMACS_ATTRIBUTE_NONNULL(1); 420 | ]# 421 | 422 | #[ 423 | /* Vector functions. */ 424 | emacs_value (*vec_get) (emacs_env *env, emacs_value vec, ptrdiff_t i) 425 | EMACS_ATTRIBUTE_NONNULL(1); 426 | 427 | void (*vec_set) (emacs_env *env, emacs_value vec, ptrdiff_t i, 428 | emacs_value val) 429 | EMACS_ATTRIBUTE_NONNULL(1); 430 | 431 | ptrdiff_t (*vec_size) (emacs_env *env, emacs_value vec) 432 | EMACS_ATTRIBUTE_NONNULL(1); 433 | ]# 434 | 435 | # vec_size, vec_get 436 | emacs.defun(vector_eq, 2): 437 | var 438 | vec = args[0] 439 | val = args[1] 440 | size = env.vec_size(env, vec) 441 | for i in 0 ..< size: 442 | if not env.eq(env, env.vec_get(env, vec, i), val): 443 | result = env.intern(env, "nil") 444 | result = env.intern(env, "t") 445 | 446 | # vec_size, vec_set 447 | emacs.defun(vector_fill, 2): 448 | var 449 | vec = args[0] 450 | val = args[1] 451 | size = env.vec_size(env, vec) 452 | 453 | for i in 0 ..< size: 454 | env.vec_set(env, vec, i, val) 455 | result = env.intern(env, "t") 456 | 457 | #[ 458 | /* Returns whether a quit is pending. */ 459 | bool (*should_quit) (emacs_env *env) 460 | EMACS_ATTRIBUTE_NONNULL(1); 461 | }; 462 | ]# 463 | 464 | 465 | emacs.provide() # (provide 'modtest) 466 | -------------------------------------------------------------------------------- /test/nim.cfg: -------------------------------------------------------------------------------- 1 | path:"../" 2 | -------------------------------------------------------------------------------- /test/return42.nim: -------------------------------------------------------------------------------- 1 | #[ 2 | 3 | License: 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | 18 | ]# 19 | 20 | # `plugin_is_GPL_compatible` indicates that its code is 21 | # released under the GPL or compatible license; Emacs will refuse to 22 | # load modules that don't export such a symbol. 23 | {.emit:"int plugin_is_GPL_compatible;".} 24 | 25 | import emacs_module # primitive wrapper for emacs_module.h 26 | 27 | init(emacs) 28 | 29 | ##### END OF BOILERPLATE CODE ##### 30 | 31 | 32 | # Return 42 33 | # This sample Nim program mimics the example in 34 | # http://diobla.info/blog-archive/modules-tut.html. 35 | 36 | #[ C code from http://diobla.info/blog-archive/modules-tut.html 37 | 38 | static emacs_value 39 | Fmymod_test (emacs_env *env, int nargs, emacs_value args[], void *data) 40 | { 41 | return env->make_integer (env, 42); 42 | } 43 | ]# 44 | # This will be accessed as (return42-return42) from Emacs. 45 | # For functions in foo.nim, all Emacs-side references of those get 46 | # prepended with "foo-". 47 | emacs.defun(return42, 0): 48 | env.make_integer(env, 42) 49 | 50 | 51 | ### You always need the below. If this file's name is foo.nim, below 52 | ### acts like the Elisp (provide 'foo). 53 | emacs.provide() 54 | -------------------------------------------------------------------------------- /test/sample.nim: -------------------------------------------------------------------------------- 1 | #[ 2 | 3 | License: 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | 18 | ]# 19 | 20 | # `plugin_is_GPL_compatible` indicates that its code is 21 | # released under the GPL or compatible license; Emacs will refuse to 22 | # load modules that don't export such a symbol. 23 | {.emit:"int plugin_is_GPL_compatible;".} 24 | 25 | import emacs_module # primitive wrapper for emacs_module.h 26 | 27 | init(emacs) 28 | 29 | # intern 30 | emacs.defun(mod_test_return_t, 1): 31 | env.intern(env, "t".cstring) 32 | 33 | # extract_integer 34 | emacs.defun(mod_test_sum, 2): 35 | assert(nargs == 2) 36 | let 37 | a = env.extract_integer(env, args[0]) 38 | b = env.extract_integer(env, args[1]) 39 | s = a + b 40 | 41 | env.make_integer(env, s) 42 | 43 | # vec_size 44 | emacs.defun(mod_test_vector_fill, 2): 45 | var 46 | vec = args[0] 47 | val = args[1] 48 | size = env.vec_size(env, vec) 49 | 50 | for i in 0 ..< size: 51 | env.vec_set(env, vec, i, val) 52 | result = env.intern(env, "t") 53 | 54 | # vec_get 55 | emacs.defun(mod_test_vector_eq, 2): 56 | var 57 | vec = args[0] 58 | val = args[1] 59 | size = env.vec_size(env, vec) 60 | for i in 0 ..< size: 61 | if not env.eq(env, env.vec_get(env, vec, i), val): 62 | result = env.intern(env, "nil") 63 | result = env.intern(env, "t") 64 | 65 | # non_local_exit_signal 66 | emacs.defun(mod_test_signal, 0): 67 | assert(env.non_local_exit_check(env) == emacs_funcall_exit_return) 68 | env.non_local_exit_signal(env, env.intern(env, "error"), 69 | env.make_integer(env, 100)) 70 | 71 | emacs.defun(mod_test_throw, 0): 72 | assert(env.non_local_exit_check(env) == emacs_funcall_exit_return) 73 | env.non_local_exit_throw(env, env.intern(env, "tag"), 74 | env.make_integer(env, 42)) 75 | result = env.intern(env, "nil") 76 | 77 | # copy_string_contents 78 | emacs.defun(mod_test_return_uname_cmd, 1): 79 | var l: ptrdiff_t 80 | if (env.copy_string_contents(env, args[0], nil, addr l)): 81 | var buf1 = newString(l-1) 82 | if (env.copy_string_contents(env, args[0], addr buf1[0], addr l)): 83 | var res = "uname " & $buf1 84 | result = env.make_string(env, addr res[0], res.len) 85 | 86 | from osproc import execCmdEx 87 | from strutils import strip 88 | emacs.defun(mod_test_return_uname, 1): 89 | var l: ptrdiff_t 90 | if (env.copy_string_contents(env, args[0], nil, addr l)): 91 | var buf1 = newString(l-1) 92 | if (env.copy_string_contents(env, args[0], addr buf1[0], addr l)): 93 | var (res, _) = execCmdEx("uname " & $buf1 ) 94 | res = res.strip() 95 | result = env.make_string(env, addr res[0], res.len) 96 | 97 | emacs.provide() 98 | -------------------------------------------------------------------------------- /test/test-modtest.el: -------------------------------------------------------------------------------- 1 | ;;; test-modtest.el --- test modtest.nim -*- lexical-binding: t; -*- 2 | 3 | ;; Author: Kaushal Modi 4 | 5 | ;;; License: 6 | ;; This program is free software: you can redistribute it and/or modify 7 | ;; it under the terms of the GNU General Public License as published by 8 | ;; the Free Software Foundation, either version 3 of the License, or 9 | ;; (at your option) any later version. 10 | ;; 11 | ;; This program is distributed in the hope that it will be useful, 12 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ;; GNU General Public License for more details. 15 | ;; 16 | ;; You should have received a copy of the GNU General Public License 17 | ;; along with this program. If not, see . 18 | ;;; Commentary: 19 | 20 | ;;; Code: 21 | 22 | (require 'ert) 23 | 24 | (add-to-list 'load-path 25 | (file-name-directory (or #$ (expand-file-name (buffer-file-name))))) 26 | 27 | (require 'modtest) 28 | 29 | 30 | ;; TODO: It's not clear how to test if `modtest-globref-make' is doing 31 | ;; the right thing. 32 | (ert-deftest modtest-globref-make () 33 | (let ((refstr "")) 34 | (dotimes (i 100) 35 | (setq refstr (concat refstr "abcdefghijklmnopqrstuvwxyz"))) 36 | (should (string= refstr (modtest-globref-make))))) 37 | 38 | (ert-deftest modtest-non-local-exit-signal-test () 39 | (should-error (modtest-signal))) 40 | 41 | (ert-deftest modtest-non-local-exit-throw-test () 42 | (should (equal 43 | (catch 'tag 44 | (modtest-throw) 45 | (ert-fail "expected throw")) 46 | 42))) 47 | 48 | (ert-deftest modtest-non-local-exit-funcall () 49 | ;; emacs_funcall_exit_return 50 | (should (equal t (modtest-non-local-exit-funcall (lambda () t)))) 51 | (should (equal 123 (modtest-non-local-exit-funcall (lambda () 123)))) 52 | ;; emacs_funcall_exit_signal 53 | (should (equal '(signal user-error ("oh noes!")) 54 | (modtest-non-local-exit-funcall 55 | (lambda () 56 | (user-error "oh noes!"))))) 57 | ;; emacs_funcall_exit_throw 58 | (should (equal '(throw foo 123) 59 | (modtest-non-local-exit-funcall 60 | (lambda () 61 | (throw 'foo 123)))))) 62 | 63 | (ert-deftest modtest-make-string () 64 | (should (string= "--" (modtest-make-string 2 ?-))) 65 | (should (string= "aaaaa" (modtest-make-string 5 ?a))) 66 | (should (string= "" (modtest-make-string 0 ?a)))) 67 | 68 | (ert-deftest modtest-return-t () 69 | (should (eq t (modtest-return-t 0))) 70 | (should (eq t (modtest-return-t "abc"))) 71 | (should (eq t (modtest-return-t t))) 72 | (should (eq t (modtest-return-t nil))) 73 | (should (eq t (modtest-return-t ?a)))) 74 | 75 | (ert-deftest modtest-get-type () 76 | (should (eq 'string (modtest-get-type "abc"))) 77 | (should (eq 'integer (modtest-get-type 42))) 78 | (should (eq 'integer (modtest-get-type (point)))) 79 | (should (eq 'float (modtest-get-type 42.0))) 80 | (should (eq 'symbol (modtest-get-type nil))) 81 | (should (eq 'symbol (modtest-get-type t))) 82 | (should (eq 'symbol (modtest-get-type '()))) 83 | (should (eq 'cons (modtest-get-type (cons 1 2)))) 84 | (should (eq 'cons (modtest-get-type '(1 . 2)))) 85 | (should (eq 'cons (modtest-get-type '(1 2 3)))) ;Interestingly, this is a "cons" too. 86 | (should (eq 'cons (modtest-get-type (list 1 2 3)))) ;.. and this too! 87 | (should (eq 'window-configuration (modtest-get-type (current-window-configuration)))) 88 | (should (eq 'marker (modtest-get-type (point-marker)))) 89 | (should (eq 'buffer (modtest-get-type (current-buffer))))) 90 | 91 | (ert-deftest modtest-is-true () 92 | (should (eq nil (modtest-is-true nil))) 93 | (should (eq nil (modtest-is-true '()))) 94 | (should (eq nil (modtest-is-true ()))) 95 | (should (eq nil (modtest-is-true (not t)))) 96 | (should (eq t (modtest-is-true "abc"))) 97 | (should (eq t (modtest-is-true ""))) 98 | (should (eq t (modtest-is-true 42))) 99 | (should (eq t (modtest-is-true 42.0))) 100 | (should (eq t (modtest-is-true t))) 101 | (should (eq t (modtest-is-true (cons 1 2)))) 102 | (should (eq t (modtest-is-true '(1 . 2)))) 103 | (should (eq t (modtest-is-true '(1 2 3)))) 104 | (should (eq t (modtest-is-true (list 1 2 3))))) 105 | 106 | (ert-deftest modtest-eq () 107 | (should (eq t (modtest-eq nil nil))) 108 | (should (eq t (modtest-eq t t))) 109 | (should (eq nil (modtest-eq nil t))) 110 | (should (eq nil (modtest-eq "abc" "abc"))) ;These are *not* the same Lisp objects! 111 | (should (eq nil (modtest-eq "" nil))) 112 | (should (eq t (modtest-eq 42 42))) 113 | (should (eq t (modtest-eq ?a ?a))) 114 | (should (eq nil (modtest-eq 42 43))) 115 | (should (eq nil (modtest-eq 42.0 42.0))) ;These are *not* the same Lisp objects! 116 | (should (eq nil (modtest-eq (cons 1 2) (cons 1 2))))) ;These are *not* the same Lisp objects! 117 | 118 | (ert-deftest modtest-sum () 119 | (should (equal 10 (modtest-sum 3 7))) 120 | (should-error (modtest-sum 3) :type 'wrong-number-of-arguments) 121 | (should-error (modtest-sum "1" 2) :type 'wrong-type-argument) 122 | (should-error (modtest-sum 2 "1") :type 'wrong-type-argument) 123 | (should-error (modtest-sum 2.0 1.0) :type 'wrong-type-argument)) 124 | 125 | (ert-deftest modtest-sum-float () 126 | (should (equal 10.0 (modtest-sum-float 3.3 6.7))) 127 | (should-error (modtest-sum-float 3.0) :type 'wrong-number-of-arguments) 128 | (should-error (modtest-sum-float "1" 2) :type 'wrong-type-argument) 129 | (should-error (modtest-sum-float 2 "1") :type 'wrong-type-argument) 130 | (should-error (modtest-sum-float 2 1) :type 'wrong-type-argument)) 131 | 132 | (ert-deftest modtest-string () 133 | (should (string= "The quick brown fox jumped over the lazy dog." (modtest-lazy))) 134 | (should (string= "Hello World" (modtest-hello "World")))) 135 | 136 | (ert-deftest modtest-uname () 137 | (let ((ref-uname-a-output (progn 138 | (require 'subr-x) 139 | (string-trim (shell-command-to-string "uname -a"))))) 140 | (should (string= ref-uname-a-output (modtest-uname "-a"))))) 141 | 142 | (ert-deftest modtest-vector-test () 143 | (dolist (s '(2 10 100 1000)) 144 | (dolist (e '(42 foo "foo" 3.14)) 145 | (let* ((v-ref (make-vector 2 e)) 146 | (eq-ref (eq (aref v-ref 0) (aref v-ref 1))) 147 | (v-test (make-vector s nil))) 148 | 149 | (should (eq (modtest-vector-fill v-test e) t)) 150 | (should (eq (modtest-vector-eq v-test e) eq-ref)))))) 151 | 152 | 153 | ;;; test-modtest.el ends here 154 | -------------------------------------------------------------------------------- /test/test-return42.el: -------------------------------------------------------------------------------- 1 | (require 'ert) 2 | 3 | (add-to-list 'load-path 4 | (file-name-directory (or #$ (expand-file-name (buffer-file-name))))) 5 | 6 | (require 'return42) 7 | 8 | (ert-deftest return42-return42-cmd () 9 | (should (= 42 (return42-return42)))) 10 | -------------------------------------------------------------------------------- /test/test-sample.el: -------------------------------------------------------------------------------- 1 | ;;; test-sample.el --- test for this repository -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2015 by Yuta Yamada 4 | 5 | ;; Author: Yuta Yamada 6 | 7 | ;;; License: 8 | ;; This program is free software: you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | ;; 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | ;; 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | ;;; Commentary: 21 | 22 | ;;; Code: 23 | 24 | (require 'ert) 25 | 26 | (add-to-list 'load-path 27 | (file-name-directory (or #$ (expand-file-name (buffer-file-name))))) 28 | 29 | (require 'sample) 30 | 31 | (ert-deftest sample-mod-test-return-t () 32 | (should (eq t (sample-mod-test-return-t 0))) 33 | (should (eq t (sample-mod-test-return-t "abc"))) 34 | (should (eq t (sample-mod-test-return-t t))) 35 | (should (eq t (sample-mod-test-return-t nil))) 36 | (should (eq t (sample-mod-test-return-t ?a)))) 37 | 38 | (ert-deftest sample-mod-test-sum () 39 | (should (eq 10 (sample-mod-test-sum 3 7))) 40 | (should-error (sample-mod-test-sum "1" 2) :type 'wrong-type-argument) 41 | (should-error (sample-mod-test-sum 2 "1") :type 'wrong-type-argument)) 42 | 43 | (ert-deftest sample-mod-test-vector-test () 44 | (dolist (s '(2 10 100 1000)) 45 | (dolist (e '(42 foo "foo" 3.14)) 46 | (let* ((v-ref (make-vector 2 e)) 47 | (eq-ref (eq (aref v-ref 0) (aref v-ref 1))) 48 | (v-test (make-vector s nil))) 49 | 50 | (should (eq (sample-mod-test-vector-fill v-test e) t)) 51 | (should (eq (sample-mod-test-vector-eq v-test e) eq-ref)))))) 52 | 53 | (ert-deftest sample-mod-test-non-local-exit-signal-test () 54 | (should-error (sample-mod-test-signal))) 55 | 56 | (ert-deftest sample-mod-test-non-local-exit-throw-test () 57 | (should (equal 58 | (catch 'tag 59 | (sample-mod-test-throw) 60 | (ert-fail "expected throw")) 61 | 42))) 62 | 63 | (ert-deftest sample-mod-test-string () 64 | (should (string= "uname -a" (sample-mod-test-return-uname-cmd "-a")))) 65 | 66 | (ert-deftest sample-mod-test-uname () 67 | (require 'subr-x) 68 | (let ((ref-uname-a-output 69 | (string-trim (shell-command-to-string "uname -a")))) 70 | (should (string= ref-uname-a-output (sample-mod-test-return-uname "-a"))))) 71 | 72 | ;; Local Variables: 73 | ;; coding: utf-8 74 | ;; mode: emacs-lisp 75 | ;; no-byte-compile: t 76 | ;; End: 77 | 78 | ;;; test-sample.el ends here 79 | --------------------------------------------------------------------------------