├── .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 |
--------------------------------------------------------------------------------