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 }