1 module emacs_module.env;
2 
3 import emacs_module.deimos;
4 
5 import core.stdc.stdint : intmax_t;
6 import std.string : fromStringz;
7 import std.traits : arity, ParameterDefaults, Parameters;
8 
9 /// Returns the minimum arity among func overloads within its module.
10 private size_t minArity(alias fn)() {
11   size_t count = 0;
12   foreach (param; ParameterDefaults!fn) {
13     if (is(param == void)) {
14       ++count;
15     }
16   }
17   return count;
18 }
19 
20 /// Returns the maximum arity among func overloads within its module.
21 alias maxArity = arity;
22 
23 version (emacs_module_test) unittest {
24   void foo(int, double, string s = "");
25   static assert(maxArity!foo == 3);
26   static assert(minArity!foo == 2);
27 }
28 
29 /// Return value of EmacsEnv.nonLocalExit.
30 struct NonLocalExit {
31   emacs_value symbol;
32   emacs_value data;
33   emacs_funcall_exit kind;
34 }
35 
36 /// High-level wrapper type for emacs_env;
37 struct EmacsEnv {
38   /// Checks if payload is compatible.
39   @nogc nothrow pure @safe
40   bool ok() const {
41     return payload_ !is null && payload_.size >= emacs_env.sizeof;
42   }
43 
44   /// Memory management.
45   /// TODO(karita): Test.
46 
47   @nogc nothrow
48   emacs_value makeGlobalRef(emacs_value reference) {
49     return payload_.make_global_ref(payload_, reference);
50   }
51 
52   @nogc nothrow
53   void freeGlobalRef(emacs_value reference) {
54     payload_.free_global_ref(payload_, reference);
55   }
56 
57   /// Non-local exit handling.
58   // TODO(karita): Test.
59 
60   @nogc nothrow
61   emacs_funcall_exit nonLocalExitCheck() {
62     return payload_.non_local_exit_check(payload_);
63   }
64 
65   @nogc nothrow
66   void nonLocalExitClear() {
67     payload_.non_local_exit_clear(payload_);
68   }
69 
70   @nogc nothrow
71   NonLocalExit nonLocalExit() {
72     NonLocalExit ret;
73     ret.kind = payload_.non_local_exit_get(payload_, &ret.symbol, &ret.data);
74     return ret;
75   }
76 
77   @nogc nothrow
78   void nonLocalExitSignal(emacs_value symbol, emacs_value data) {
79     payload_.non_local_exit_signal(payload_, symbol, data);
80   }
81 
82   @nogc nothrow
83   void nonLocalExitThrow(emacs_value tag, emacs_value value) {
84     payload_.non_local_exit_throw(payload_, tag, value);
85   }
86 
87   /// Returns emacs lisp symbol by the given name.
88   @nogc nothrow
89   emacs_value intern(string name) {
90     return payload_.intern(payload_, name.ptr);
91   }
92 
93   /// Calls emacs lisp function with the given args.
94   emacs_value funcall(Args...)(string funcName, Args args) {
95     emacs_value func = intern(funcName);
96     emacs_value[Args.length] evArgs;
97     static foreach (i; 0 .. Args.length) {
98       evArgs[i] = toEmacsValue(this, args[i]);
99     }
100     return payload_.funcall(payload_, func, evArgs.length, evArgs.ptr);
101   }
102 
103   /// Wraps emacs_env.make_function with auto-detected arity.
104   /// WARNING: func overload is not supported.
105   emacs_value makeFunction(alias func)(
106       const(char)[] doc = "", void* data = null) {
107     // Wrapper function.
108     extern (C)
109     emacs_value wrapper(
110         emacs_env* env, ptrdiff_t nargs, emacs_value* args, void* data) {
111       alias Params = Parameters!func;
112       static assert(is(Params[0] == EmacsEnv),
113                     "First func argument must be EmacsEnv.");
114 
115       // Convert emacs_value to typed D value.
116       Params params;
117       params[0] = EmacsEnv(env);
118       static foreach (i; 1 .. params.length) {
119         params[i] = fromEmacsValue!(Params[i])(params[0], args[i-1]);
120       }
121       return toEmacsValue(params[0], func(params));
122     }
123 
124     return payload_.make_function(
125         payload_, minArity!func-1, maxArity!func-1, &wrapper, doc.ptr, data);
126   }
127 
128   /// Type-safe emacs lisp "defalias".
129   emacs_value defAlias(alias func)(
130       string elispName, string doc = "", void* data = null) {
131     return funcall("defalias", intern(elispName),
132                    makeFunction!func(doc, data));
133   }
134 
135   /// Returns type of the given value in emacs lisp.
136   @nogc nothrow
137   emacs_value typeOf(emacs_value value) {
138     return payload_.type_of(payload_, value);
139   }
140 
141   /// Returns true if value is the given type in emacs lisp.
142   @nogc nothrow
143   bool isTypeOf(emacs_value value, string type) {
144     return eq(typeOf(value), intern(type));
145   }
146 
147   /// Returns true if the given emacs value is not nil.
148   @nogc nothrow
149   bool isNotNil(emacs_value value) {
150     return payload_.is_not_nil(payload_, value);
151   }
152 
153   /// Returns true if two emacs_values are equal.
154   @nogc nothrow
155   bool eq(emacs_value a, emacs_value b) {
156     return payload_.eq(payload_, a, b);
157   }
158 
159   /// Vector functions.
160   T vecGet(T)(emacs_value vec, ptrdiff_t i) {
161     return fromEmacsValue!T(this, payload_.vec_get(payload_, vec, i));
162   }
163 
164   void vecSet(T)(emacs_value vec, ptrdiff_t i, T value) {
165     payload_.vec_set(payload_, vec, i, toEmacsValue(this, value));
166   }
167 
168   @nogc nothrow
169   ptrdiff_t vecSize(emacs_value vec) {
170     return payload_.vec_size(payload_, vec);
171   }
172 
173 
174  private:
175   /// C API payload.
176   emacs_env* payload_ = null;
177 }
178 
179 /// Type conversion. These shouldn't be methods in EmacsEnv because makeFunction
180 /// can wrap only `function` NOT `delegate`, which captures `this`.
181 /// TODO(karita): Support embedded pointer type.
182 
183 /// Converts emacs_value to emacs_value.
184 @nogc nothrow pure @safe
185 inout(emacs_value) toEmacsValue(const(EmacsEnv) env, inout(emacs_value) value) {
186   return value;
187 }
188 
189 /// ditto
190 @nogc nothrow pure @safe
191 inout(emacs_value) fromEmacsValue(T: emacs_value)(
192     const(EmacsEnv) env, inout(emacs_value) value) { return value;
193 }
194 
195 /// Converts intmax_t to emacs_value.
196 @nogc nothrow
197 emacs_value toEmacsValue(EmacsEnv env, intmax_t value) {
198   return env.payload_.make_integer(env.payload_, value);
199 }
200 
201 /// Converts emacs_value to intmax_t.
202 intmax_t fromEmacsValue(T: intmax_t)(EmacsEnv env, emacs_value value) {
203   return env.payload_.extract_integer(env.payload_, value);
204 }
205 
206 /// Converts double to emacs_value.
207 @nogc nothrow
208 emacs_value toEmacsValue(EmacsEnv env, double value) {
209   return env.payload_.make_float(env.payload_, value);
210 }
211 
212 /// Converts emacs_value to double.
213 double fromEmacsValue(T: double)(EmacsEnv env, emacs_value value) {
214   return env.payload_.extract_float(env.payload_, value);
215 }
216 
217 /// Converts bool to emacs_value.
218 @nogc nothrow
219 emacs_value toEmacsValue(EmacsEnv env, bool value) {
220   return env.intern(value ? "t" : "nil");
221 }
222 
223 /// Converts emacs_value to bool. Note that all non-nil values are true.
224 bool fromEmacsValue(T: bool)(EmacsEnv env, emacs_value value) {
225   return env.isNotNil(value);
226 }
227 
228 /// Converts emacs_value to string.
229 @nogc nothrow
230 emacs_value toEmacsValue(EmacsEnv env, string value) {
231   return env.payload_.make_string(env.payload_, value.ptr, value.length);
232 }
233 
234 /// Converts string to emacs_value.
235 string fromEmacsValue(T: string)(EmacsEnv env, emacs_value ev) {
236   // Ask string size.
237   ptrdiff_t size;
238   env.payload_.copy_string_contents(env.payload_, ev, null, &size);
239   if (size == 0) return "";
240 
241   // Copy string to new allocated buffer.
242   auto buf = new char[size];
243   env.payload_.copy_string_contents(env.payload_, ev, buf.ptr, &size);
244   assert(size == buf.length);
245 
246   // Omit the last NULL byte.
247   return buf.idup[0 .. size - 1];
248 }
249 
250 /// Emacs vector type.
251 struct EmacsVec(T = emacs_value) {
252   /// Gets a value at the given index.
253   T opIndex(ptrdiff_t i) { return env.vecGet!T(vec, i); }
254 
255   /// Sets a value at the given index.
256   T opIndexAssign(T value, ptrdiff_t i) {
257     env.vecSet(vec, i, value);
258     return value;
259   }
260 
261   /// Returns the length of the vector.
262   @nogc nothrow
263   ptrdiff_t length() { return env.vecSize(vec); }
264 
265   /// ditto
266   ptrdiff_t opDollar(size_t dim : 0)() { return length; }
267 
268  private:
269   emacs_value vec;
270   EmacsEnv env;
271 }
272 
273 /// Converts EmacsVec!T to emacs_value.
274 @nogc nothrow
275 emacs_value toEmacsValue(V : EmacsVec!T, T)(const(EmacsEnv) env, V value) {
276   return value.vec;
277 }
278 
279 /// Converts emacs_value to EmacsVec!T.
280 EmacsVec!T fromEmacsValue(V : EmacsVec!T, T)(EmacsEnv env, emacs_value value) {
281   return EmacsVec!T(value, env);
282 }