1 /* lua/util.i: custom lua typemaps for xapian-bindings
2  *
3  * Copyright (C) 2011 Xiaona Han
4  * Copyright (C) 2012 Olly Betts
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (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, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21 
22 // "end" is a keyword in Lua, so we rename it to "_end"
23 %rename("_end") end;
24 
25 %rename("__tostring") get_description;
26 
27 %{
28 #if LUA_VERSION_NUM-0 >= 502
29 // luaL_typerror was removed in Lua 5.2.
luaL_typerror(lua_State * L,int narg,const char * tname)30 int luaL_typerror (lua_State *L, int narg, const char *tname) {
31   const char *msg = lua_pushfstring(L, "%s expected, got %s",
32                                     tname, luaL_typename(L, narg));
33   return luaL_argerror(L, narg, msg);
34 }
35 #endif
36 
37 // Define lua_rawlen for Lua 5.1 (Lua 5.2 already has it).
38 //
39 // Newer SWIG already does this, hence the check for !defined lua_rawlen.
40 #if !defined lua_rawlen && LUA_VERSION_NUM == 501
41 # define lua_rawlen lua_objlen
42 #endif
43 
44 // LUA_RIDX_GLOBALS is new in Lua 5.2.
45 #ifdef LUA_RIDX_GLOBALS
46 # ifndef LUA_GLOBALSINDEX
47 #  define LUA_GLOBALSINDEX (-10002)
48 # endif
49 #define lua_pushvalue(L,P) do { if ((P) == LUA_GLOBALSINDEX) { lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); } else { lua_pushvalue(L,P); } } while (0)
50 #endif
51 %}
52 
53 %define SUB_CLASS(NS, CLASS)
54 %{
55 class lua##CLASS : public NS::CLASS {
56 	int r;
57 	lua_State* L;
58 
59 	public:
CLASS(lua_State * S)60 		lua##CLASS(lua_State* S) {
61 			L = S;
62 			if (!lua_isfunction(L, -1)) {
63 				luaL_typerror(L, -1, "function");
64 			}
65 			r = luaL_ref(L, LUA_REGISTRYINDEX);
66 		}
67 
CLASS()68 		~lua##CLASS() {
69 			luaL_unref(L, LUA_REGISTRYINDEX, r);
70 		}
71 
operator()72 		bool operator()(const std::string &term) const {
73 			lua_rawgeti(L, LUA_REGISTRYINDEX, r);
74 			if (!lua_isfunction(L, -1)) {
75 				luaL_typerror(L, -1, "function");
76 			}
77 
78 			lua_pushlstring(L, (char *)term.c_str(), term.length());
79 			if (lua_pcall(L, 1, 1, 0) != 0){
80 				luaL_error(L, "error running function: %s", lua_tostring(L, -1));
81 			}
82 			if (!lua_isboolean(L, -1)) {
83 				luaL_error(L, "function must return a boolean");
84 			}
85 			bool result = lua_toboolean(L, -1);
86 			lua_pop(L, 1);
87 			return result;
88 		}
89 };
90 %}
91 
92 %enddef
93 
94 SUB_CLASS(Xapian, ExpandDecider)
95 SUB_CLASS(Xapian, Stopper)
96 
97 %{
98 class luaMatchDecider : public Xapian::MatchDecider {
99 	int r;
100 	lua_State* L;
101 
102 	public:
luaMatchDecider(lua_State * S)103 		luaMatchDecider(lua_State* S) {
104 			L = S;
105 			if (!lua_isfunction(L, -1)) {
106 				luaL_typerror(L, -1, "function");
107 			}
108 			r = luaL_ref(L, LUA_REGISTRYINDEX);
109 		}
110 
~luaMatchDecider()111 		~luaMatchDecider() {
112 			luaL_unref(L, LUA_REGISTRYINDEX, r);
113 		}
114 
operator()115 		bool operator()(const Xapian::Document &doc) const {
116 			lua_rawgeti(L, LUA_REGISTRYINDEX, r);
117 			if (!lua_isfunction(L, -1)) {
118 				luaL_typerror(L, -1, "function");
119 			}
120 
121 			SWIG_NewPointerObj(L, &doc, SWIGTYPE_p_Xapian__Document, 0);
122 			if (lua_pcall(L, 1, 1, 0) != 0){
123 				luaL_error(L, "error running function: %s", lua_tostring(L, -1));
124 			}
125 			if (!lua_isboolean(L, -1)) {
126 				luaL_error(L, "function must return a boolean");
127 			}
128 			bool result = lua_toboolean(L, -1);
129 			lua_pop(L, 1);
130 			return result;
131 		}
132 };
133 %}
134 
135 %{
136 class luaStemImplementation : public Xapian::StemImplementation {
137 	int r;
138 	lua_State* L;
139 
140 	public:
luaStemImplementation(lua_State * S)141 		luaStemImplementation(lua_State* S) {
142 			L = S;
143 			if (!lua_isfunction(L, -1)) {
144 				luaL_typerror(L, -1, "function");
145 			}
146 			r = luaL_ref(L, LUA_REGISTRYINDEX);
147 		}
148 
~luaStemImplementation()149 		~luaStemImplementation() {
150 			luaL_unref(L, LUA_REGISTRYINDEX, r);
151 		}
152 
operator()153 		std::string operator()(const std::string &word) {
154 			lua_rawgeti(L, LUA_REGISTRYINDEX, r);
155 			if (!lua_isfunction(L, -1)) {
156 				luaL_typerror(L, -1, "function");
157 			}
158 
159 			lua_pushlstring(L, (char *)word.c_str(), word.length());
160 			if (lua_pcall(L, 1, 1, 0) != 0){
161 				luaL_error(L, "error running function: %s", lua_tostring(L, -1));
162 			}
163 			if (!lua_isstring(L, -1)) {
164 				luaL_error(L, "function must return a string");
165 			}
166 			size_t len;
167 			const char * p = lua_tolstring(L, -1, &len);
168 			std::string result(p, len);
169 			lua_pop(L, 1);
170 			return result;
171 		}
get_description()172 		std::string get_description() const {
173 			lua_rawgeti(L, LUA_REGISTRYINDEX, r);
174 			if (!lua_isfunction(L, -1)) {
175 				luaL_typerror(L, -1, "function");
176 			}
177 
178 			if (lua_pcall(L, 0, 1, 0) != 0){
179 				luaL_error(L, "error running function: %s", lua_tostring(L, -1));
180 			}
181 			if (!lua_isstring(L, -1)) {
182 				luaL_error(L, "function must return a string");
183 			}
184 
185 			size_t len;
186 			const char * p = lua_tolstring(L, -1, &len);
187 			std::string result(p, len);
188 			lua_pop(L, 1);
189 			return result;
190 		}
191 };
192 %}
193 
194 %{
195 class luaKeyMaker : public Xapian::KeyMaker {
196 	int r;
197 	lua_State* L;
198 
199 	public:
luaKeyMaker(lua_State * S)200 		luaKeyMaker(lua_State* S) {
201 			L = S;
202 			if (!lua_isfunction(L, -1)) {
203 				luaL_typerror(L, -1, "function");
204 			}
205 			r = luaL_ref(L, LUA_REGISTRYINDEX);
206 		}
207 
~luaKeyMaker()208 		~luaKeyMaker() {
209 			luaL_unref(L, LUA_REGISTRYINDEX, r);
210 		}
211 
operator()212 		std::string operator()(const Xapian::Document &doc) const {
213 			lua_rawgeti(L, LUA_REGISTRYINDEX, r);
214 			if (!lua_isfunction(L, -1)) {
215 				luaL_typerror(L, -1, "function");
216 			}
217 
218 			SWIG_NewPointerObj(L, &doc, SWIGTYPE_p_Xapian__Document, 0);
219 			if (lua_pcall(L, 1, 1, 0) != 0){
220 				luaL_error(L, "error running function: %s", lua_tostring(L, -1));
221 			}
222 			if (!lua_isstring(L, -1)) {
223 				luaL_error(L, "function must return a string");
224 			}
225 			size_t len;
226 			const char * p = lua_tolstring(L, -1, &len);
227 			std::string result(p, len);
228 			lua_pop(L, 1);
229 			return result;
230 		}
231 };
232 %}
233 
234 %{
235 class luaValueRangeProcessor : public Xapian::ValueRangeProcessor {
236 	int r;
237 	lua_State* L;
238 
239 	public:
luaValueRangeProcessor(lua_State * S)240 		luaValueRangeProcessor(lua_State* S) {
241 			L = S;
242 			if (!lua_isfunction(L, -1)) {
243 				luaL_typerror(L, -1, "function");
244 			}
245 			r = luaL_ref(L, LUA_REGISTRYINDEX);
246 		}
247 
~luaValueRangeProcessor()248 		~luaValueRangeProcessor() {
249 			luaL_unref(L, LUA_REGISTRYINDEX, r);
250 		}
251 
operator()252 		Xapian::valueno operator()(std::string &begin, std::string &end) {
253 			lua_rawgeti(L, LUA_REGISTRYINDEX, r);
254 			if (!lua_isfunction(L, -1)) {
255 				luaL_typerror(L, -1, "function");
256 			}
257 
258 			lua_pushlstring(L, (char *)begin.c_str(), begin.length());
259 			lua_pushlstring(L, (char *)end.c_str(), end.length());
260 
261 			if (lua_pcall(L, 2, 1, 0) != 0){
262 				luaL_error(L, "error running function: %s", lua_tostring(L, -1));
263 			}
264 			if (!lua_isnumber(L, -1)) {
265 				luaL_error(L, "function must return a nubmer");
266 			}
267 			Xapian::valueno result(lua_tonumber(L, -1));
268 			lua_pop(L, 1);
269 			return result;
270 		}
271 };
272 %}
273 
274 %{
275 class luaMatchSpy : public Xapian::MatchSpy {
276 	int r;
277 	lua_State* L;
278 
279 	public:
luaMatchSpy(lua_State * S)280 		luaMatchSpy(lua_State* S) {
281 			L = S;
282 			if (!lua_isfunction(L, -1)) {
283 				luaL_typerror(L, -1, "function");
284 			}
285 			r = luaL_ref(L, LUA_REGISTRYINDEX);
286 		}
287 
~luaMatchSpy()288 		~luaMatchSpy() {
289 			luaL_unref(L, LUA_REGISTRYINDEX, r);
290 		}
291 
operator()292 		void operator()(const Xapian::Document &doc, Xapian::weight wt) {
293 			lua_rawgeti(L, LUA_REGISTRYINDEX, r);
294 			if (!lua_isfunction(L, -1)) {
295 				luaL_typerror(L, -1, "function");
296 			}
297 
298 			SWIG_NewPointerObj(L, &doc, SWIGTYPE_p_Xapian__Document, 0);
299 			SWIG_NewPointerObj(L, &wt, SWIGTYPE_p_Xapian__Weight, 0);
300 			if (lua_pcall(L, 2, 1, 0) != 0){
301 				luaL_error(L, "error running function: %s", lua_tostring(L, -1));
302 			}
303 			return;
304 		}
305 };
306 %}
307 
308 %define SUB_CLASS_TYPEMAPS(NS, CLASS)
309 
310 %typemap(typecheck, precedence=100) NS::CLASS * {
311 	void *ptr;
312 	if (lua_isfunction(L, $input) || (SWIG_isptrtype(L, $input) && !SWIG_ConvertPtr(L, $input, (void **) &ptr, $descriptor(NS::CLASS *), 0))) {
313 		$1 = 1;
314 	}
315 	else {
316 		$1 = 0;
317 	}
318 }
319 %typemap(in) NS::CLASS * {
320 	if (lua_isfunction(L, $input)) {
321 		$1 = new lua##CLASS(L);
322 	}
323 	else {
324 		if (!SWIG_IsOK(SWIG_ConvertPtr(L, $input, (void**)&$1, $descriptor(NS::CLASS *), 0))){
325 			SWIG_fail;
326 		}
327 	}
328 }
329 
330 %enddef
331 SUB_CLASS_TYPEMAPS(Xapian, MatchDecider)
332 SUB_CLASS_TYPEMAPS(Xapian, ExpandDecider)
333 SUB_CLASS_TYPEMAPS(Xapian, Stopper)
334 SUB_CLASS_TYPEMAPS(Xapian, StemImplementation)
335 SUB_CLASS_TYPEMAPS(Xapian, KeyMaker)
336 SUB_CLASS_TYPEMAPS(Xapian, ValueRangeProcessor)
337 
338 %luacode {
339 function xapian.Iterator(begin, _end)
340 	local iter = begin;
341 	local isFirst = 1
342 	return function()
343 		if iter:equals(_end) then
344 			return nil
345 		else
346 			if isFirst == 1 then
347 				isFirst = 0;
348 				return iter
349 			else
350 				iter:next()
351 				if iter:equals(_end) then
352 					return nil
353 				end
354 				return iter
355 			end
356 		end
357 	end
358 end
359 }
360 
361 #define XAPIAN_MIXED_VECTOR_QUERY_INPUT_TYPEMAP
362 
363 %typemap(typecheck, precedence=500) const vector<Xapian::Query> & {
364     $1 = lua_istable(L, $input);
365     /* FIXME: if we add more array typemaps, we'll need to check the elements
366      * of the array here to disambiguate. */
367 }
368 
369 
370 %typemap(in) const vector<Xapian::Query> & (vector<Xapian::Query> v) {
371     int numitems = lua_rawlen(L, $input);;
372     v.reserve(numitems);
373     for (int i = 0; i < numitems; ++i) {
374         lua_rawgeti(L, $input, i+1);
375         if (lua_isstring(L, -1)) {
376             size_t len = 0;
377             const char *p = lua_tolstring(L, -1, &len);
378             v.push_back(Xapian::Query(string(p, len)));
379         } else {
380             Xapian::Query *subq = 0;
381             if(!lua_isuserdata(L, -1) || SWIG_ConvertPtr(L, -1, (void **)&subq, SWIGTYPE_p_Xapian__Query, 0) == -1){
382                 lua_pop(L, 1);
383                 luaL_argerror(L, $input, "elements of Tables passed to Query must be either Strings or other Queries");
384             }
385 
386             v.push_back(*subq);
387         }
388         lua_pop(L, 1);
389     }
390     $1 = &v;
391 }
392 
393 %define OUTPUT_ITERATOR_METHODS(NS, CLASS, ITERATOR_CLASS, ITERATOR_BEGIN, ITERATOR_END, DEREF_METHOD, PARAMETER_NAME, PARAMETER_VALUE)
394 
395 %extend NS::CLASS {
396 	std::pair<NS::ITERATOR_CLASS , NS::ITERATOR_CLASS> DEREF_METHOD(PARAMETER_NAME) {
397 		return std::make_pair($self->ITERATOR_BEGIN(PARAMETER_VALUE), $self->ITERATOR_END(PARAMETER_VALUE));
398 	}
399 }
400 
401 %typemap(out) std::pair<NS::ITERATOR_CLASS, NS::ITERATOR_CLASS> {
402 	lua_getglobal(L, "xapian");
403 	lua_pushstring(L, "Iterator");
404 	lua_gettable(L, -2);
405 	lua_remove(L, -2);
406 
407 	if (!lua_isfunction(L, -1)) {
408 				luaL_typerror(L, -1, "function");
409 	}
410 
411 	NS::ITERATOR_CLASS * begin = new NS::ITERATOR_CLASS((const NS::ITERATOR_CLASS &)$1.first);
412 	SWIG_NewPointerObj(L, (void *) begin, $descriptor(NS::ITERATOR_CLASS *), 1);
413 
414 	NS::ITERATOR_CLASS * end = new NS::ITERATOR_CLASS((const NS::ITERATOR_CLASS &)$1.second);
415 	SWIG_NewPointerObj(L, (void *) end, $descriptor(NS::ITERATOR_CLASS *), 1);
416 
417 	if (lua_pcall(L, 2, 1, 0) != 0) {
418 		luaL_error(L, "error running function: %s", lua_tostring(L, -1));
419 	}
420 
421 	SWIG_arg++;
422 }
423 
424 %enddef
425 
426 OUTPUT_ITERATOR_METHODS(Xapian, Query, TermIterator, get_terms_begin, get_terms_end, get_terms, void, )
427 
428 OUTPUT_ITERATOR_METHODS(Xapian, QueryParser, TermIterator, stoplist_begin, stoplist_end, stoplist, void, )
429 
430 OUTPUT_ITERATOR_METHODS(Xapian, ESet, ESetIterator, begin, end, terms, void, )
431 
432 OUTPUT_ITERATOR_METHODS(Xapian, MSet, MSetIterator, begin, end, items, void, )
433 
434 OUTPUT_ITERATOR_METHODS(Xapian, Document, TermIterator, termlist_begin, termlist_end, termlist, void, )
435 OUTPUT_ITERATOR_METHODS(Xapian, Document, ValueIterator, values_begin, values_end, values, void, )
436 
437 OUTPUT_ITERATOR_METHODS(Xapian, Enquire, TermIterator, get_matching_terms_begin, get_matching_terms_end, get_matching_terms, Xapian::docid did, did)
438 OUTPUT_ITERATOR_METHODS(Xapian, Enquire, TermIterator, get_matching_terms_begin, get_matching_terms_end, get_matching_terms, const MSetIterator &it, it)
439 
440 OUTPUT_ITERATOR_METHODS(Xapian, ValueCountMatchSpy, TermIterator, values_begin, values_end, values, void, )
441 OUTPUT_ITERATOR_METHODS(Xapian, ValueCountMatchSpy, TermIterator, top_values_begin, top_values_end, top_values, size_t maxvalues, maxvalues)
442 
443 OUTPUT_ITERATOR_METHODS(Xapian, Database, TermIterator, allterms_begin, allterms_end, allterms, void, )
444 OUTPUT_ITERATOR_METHODS(Xapian, Database, TermIterator, spellings_begin, spellings_end, spellings, void, )
445 OUTPUT_ITERATOR_METHODS(Xapian, Database, PostingIterator, postlist_begin, postlist_end, postlist, const std::string &tname, tname)
446 OUTPUT_ITERATOR_METHODS(Xapian, Database, TermIterator, termlist_begin, termlist_end, termlist, Xapian::docid did, did)
447 OUTPUT_ITERATOR_METHODS(Xapian, Database, ValueIterator, valuestream_begin, valuestream_end, valuestream, Xapian::valueno slot, slot)
448 OUTPUT_ITERATOR_METHODS(Xapian, Database, TermIterator, allterms_begin, allterms_end, allterms, const std::string &prefix, prefix)
449 OUTPUT_ITERATOR_METHODS(Xapian, Database, TermIterator, synonyms_begin, synonyms_end, synonyms, const std::string &term, term)
450 OUTPUT_ITERATOR_METHODS(Xapian, Database, TermIterator, synonym_keys_begin, synonym_keys_end, synonym_keys, const std::string &prefix, prefix)
451 OUTPUT_ITERATOR_METHODS(Xapian, Database, TermIterator, metadata_keys_begin, metadata_keys_end, metadata_keys, const std::string &prefix, prefix)
452 
453 %extend Xapian::Database {
454 	std::pair<Xapian::PositionIterator , Xapian::PositionIterator> positionlist(Xapian::docid did, const std::string &tname) {
455 		return std::make_pair($self->positionlist_begin(did, tname), $self->positionlist_end(did, tname));
456 	}
457 }
458 
459 %typemap(out) std::pair<Xapian::PositionIterator, Xapian::PositionIterator> {
460 	lua_getglobal(L, "xapian");
461 	lua_pushstring(L, "Iterator");
462 	lua_gettable(L, -2);
463 	lua_remove(L, -2);
464 
465 	if (!lua_isfunction(L, -1)) {
466 		luaL_typerror(L, -1, "function");
467 	}
468 
469 	Xapian::PositionIterator * begin = new Xapian::PositionIterator((const Xapian::PositionIterator &)$1.first);
470 	SWIG_NewPointerObj(L, (void *) begin, SWIGTYPE_p_Xapian__PositionIterator, 1);
471 
472 	Xapian::PositionIterator * end = new Xapian::PositionIterator((const Xapian::PositionIterator &)$1.second);
473 	SWIG_NewPointerObj(L, (void *) end, SWIGTYPE_p_Xapian__PositionIterator, 1);
474 
475 	if (lua_pcall(L, 2, 1, 0) != 0) {
476 		luaL_error(L, "error running function: %s", lua_tostring(L, -1));
477 	}
478 
479 	SWIG_arg++;
480 }
481