]> Dogcows Code - chaz/yoink/blob - src/moof/script.hh
the massive refactoring effort
[chaz/yoink] / src / moof / script.hh
1
2 /*] Copyright (c) 2009-2010, Charles McGarvey [**************************
3 **] All rights reserved.
4 *
5 * vi:ts=4 sw=4 tw=75
6 *
7 * Distributable under the terms and conditions of the 2-clause BSD license;
8 * see the file COPYING for a complete text of the license.
9 *
10 **************************************************************************/
11
12 #ifndef _MOOF_SCRIPT_HH_
13 #define _MOOF_SCRIPT_HH_
14
15 /**
16 * \file script.hh
17 * A thin wrapper over Lua. This is not meant as a complicated binding
18 * package between C++ and Lua. It is not meant to obscure the division
19 * between C++ and Lua but rather to clarify it and make it more
20 * manageable. It does not hide the concept of the Lua stack, but rather
21 * provides that mechanism with a certain level of abstraction while also
22 * providing a cleaner, more consistent API.
23 */
24
25 #include <iostream>
26 #include <list>
27 #include <map>
28 #include <string>
29 #include <vector>
30
31 #include <boost/bind.hpp>
32 #include <boost/function.hpp>
33 #include <boost/shared_ptr.hpp>
34 #include <lua.hpp>
35
36
37 namespace moof {
38
39
40 class script;
41 typedef boost::shared_ptr<script> script_ptr;
42
43
44 class script
45 {
46 public:
47
48 typedef boost::function<int(script&)> function;
49
50 enum status
51 {
52 success = 0,
53 yielding = LUA_YIELD,
54 runtime_error = LUA_ERRRUN,
55 syntax_error = LUA_ERRSYNTAX,
56 memory_error = LUA_ERRMEM,
57 handler_error = LUA_ERRERR,
58 file_error = LUA_ERRFILE
59 };
60
61 enum pseudoindex
62 {
63 registry_index = LUA_REGISTRYINDEX,
64 environment_index = LUA_ENVIRONINDEX,
65 globals_index = LUA_GLOBALSINDEX
66 };
67
68 /**
69 * This is the most prominent abstraction on top of the standard Lua
70 * API. A slot object represents a value on the stack. More
71 * specifically, it represents a position on the stack. The
72 * distinction is only important when objects are moved around on the
73 * stack or if the slot represents a negative index on the stack (the
74 * value of which will change as things are pushed onto and popped from
75 * the stack).
76 */
77
78 struct slot
79 {
80 /**
81 * You have direct access to the index of the value on the stack
82 * being represented.
83 */
84
85 int index;
86
87
88 enum type
89 {
90 none = LUA_TNONE,
91 nil = LUA_TNIL,
92 boolean = LUA_TBOOLEAN,
93 light_data = LUA_TLIGHTUSERDATA,
94 number = LUA_TNUMBER,
95 string = LUA_TSTRING,
96 table = LUA_TTABLE,
97 function = LUA_TFUNCTION,
98 data = LUA_TUSERDATA,
99 thread = LUA_TTHREAD
100 };
101
102
103 slot(const class script& s, int i = 0) :
104 index(i),
105 script_(const_cast<class script&>(s)) {}
106
107 /**
108 * A copied value presently points to the same value, except the
109 * real index is used. That means that if a value that refers to a
110 * frame referenced from the top of the stack will have its
111 * normalized index copied into the new value object.
112 */
113
114 //slot(const slot& copy) :
115 //index(copy.positiveIndex()),
116 //script_(copy.script_) {}
117
118
119 // check the type of the value
120 bool is_boolean() const
121 { return (bool)lua_isboolean(script_.state_, index); }
122 bool is_imported_function() const
123 { return (bool)lua_iscfunction(script_.state_, index); }
124 bool is_function() const
125 { return (bool)lua_isfunction(script_.state_, index); }
126 bool is_nil() const
127 { return (bool)lua_isnil(script_.state_, index); }
128 bool is_none() const
129 { return (bool)lua_isnone(script_.state_, index); }
130 bool is_none_or_nil() const
131 { return (bool)lua_isnoneornil(script_.state_, index); }
132 bool is_number() const
133 { return (bool)lua_isnumber(script_.state_, index); }
134 bool is_string() const
135 { return (bool)lua_isstring(script_.state_, index); }
136 bool is_table() const
137 { return (bool)lua_istable(script_.state_, index); }
138 bool is_thread() const
139 { return (bool)lua_isthread(script_.state_, index); }
140 bool is_data() const
141 { return (bool)lua_isuserdata(script_.state_, index); }
142 bool is_light_data() const
143 { return (bool)lua_islightuserdata(script_.state_, index); }
144
145 /**
146 * Check the value and throw an error if its the wrong type.
147 * There's a little caveat: This method never returns because it
148 * does a long jump. Consequently, constructed C++ objects which
149 * exist on the stack between the current frame and some lua
150 * function will not be destructed. That's not a problem for
151 * objects that only exist on the stack, but any objects that
152 * allocate memory on the heap (such as containers or strings) will
153 * leak. Therefore, you should only call this method after
154 * cleaning up such objects. The best thing to do for defining
155 * functions is to simply check all the parameters at the get-go
156 * before any C++ objects are even constructed.
157 */
158
159 void require_type(type t) const
160 {
161 if (t != type())
162 {
163 luaL_typerror(script_.state_, index,
164 lua_typename(script_.state_, t));
165 }
166 }
167
168 void raise(const char* error)
169 {
170 luaL_argerror(script_.state_, index, error);
171 }
172
173
174 slot& require_boolean()
175 {
176 if (!is_boolean())
177 {
178 luaL_typerror(script_.state_, index, "boolean");
179 }
180 return *this;
181 }
182 slot& require_number()
183 {
184 if (!is_number())
185 {
186 luaL_typerror(script_.state_, index, "number");
187 }
188 return *this;
189 }
190 slot& require_string()
191 {
192 if (!is_string())
193 {
194 luaL_typerror(script_.state_, index, "string");
195 }
196 return *this;
197 }
198 slot& require_table()
199 {
200 if (!is_table())
201 {
202 luaL_typerror(script_.state_, index, "table");
203 }
204 return *this;
205 }
206 slot& require_function()
207 {
208 if (!is_function())
209 {
210 luaL_typerror(script_.state_, index, "function");
211 }
212 return *this;
213 }
214 slot& require_data()
215 {
216 if (!is_data())
217 {
218 luaL_typerror(script_.state_, index, "data");
219 }
220 return *this;
221 }
222 slot& require_nil()
223 {
224 if (!is_nil())
225 {
226 luaL_typerror(script_.state_, index, "nil");
227 }
228 return *this;
229 }
230 slot& require_thread()
231 {
232 if (!is_thread())
233 {
234 luaL_typerror(script_.state_, index, "thread");
235 }
236 return *this;
237 }
238
239
240 /**
241 * Get the type of the value.
242 */
243
244 enum type type() const
245 {
246 return (enum type)lua_type(script_.state_, index);
247 }
248
249 /**
250 * Get the name of the type of the value as a string.
251 */
252
253 std::string type_name() const
254 {
255 return std::string(luaL_typename(script_.state_, index));
256 }
257
258
259 /**
260 * Get the length of the value according to the definition given by
261 * Lua.
262 */
263
264 size_t length() const
265 {
266 return lua_objlen(script_.state_, index);
267 }
268
269 int positiveIndex() const
270 {
271 if (index < 0) return index + lua_gettop(script_.state_) + 1;
272 else return index;
273 }
274
275
276 /**
277 * Get a pointer value (for userdata, tables, threads, and
278 * functions).
279 */
280
281 const void* id() const
282 {
283 return lua_topointer(script_.state_, index);
284 }
285
286 bool is_identical(const slot& rhs) const
287 {
288 return &script_ == &(rhs.script_) && index == rhs.index;
289 }
290
291 operator bool () const
292 {
293 return !is_none();
294 }
295
296
297 bool operator == (const slot& rhs) const
298 {
299 return (bool)lua_equal(script_.state_, index, rhs.index);
300 }
301 bool operator != (const slot& rhs) const
302 {
303 return !(*this == rhs);
304 }
305
306 bool operator < (const slot& rhs) const
307 {
308 return (bool)lua_lessthan(script_.state_, index, rhs.index);
309 }
310 bool operator <= (const slot& rhs) const
311 {
312 return *this < rhs || *this == rhs;
313 }
314 bool operator > (const slot& rhs) const
315 {
316 return !(*this <= rhs);
317 }
318 bool operator >= (const slot& rhs) const
319 {
320 return !(*this < rhs);
321 }
322
323
324 /**
325 * Convert the underlying value to a C++ type.
326 */
327
328 template <class T>
329 bool get(T& value) const
330 {
331 if (is_number())
332 {
333 value = (T)lua_tointeger(script_.state_, index);
334 return true;
335 }
336 return false;
337 }
338
339 bool get(float& value) const
340 {
341 if (is_number())
342 {
343 value = (float)lua_tonumber(script_.state_, index);
344 return true;
345 }
346 return false;
347 }
348 bool get(double& value) const
349 {
350 if (is_number())
351 {
352 value = (double)lua_tonumber(script_.state_, index);
353 return true;
354 }
355 return false;
356 }
357
358 bool get(bool& value) const
359 {
360 if (is_boolean())
361 {
362 value = (bool)lua_toboolean(script_.state_, index);
363 return true;
364 }
365 return false;
366 }
367
368 bool get(const char*& value, size_t& size) const
369 {
370 if (is_string())
371 {
372 value = lua_tolstring(script_.state_, index, &size);
373 return true;
374 }
375 return false;
376 }
377
378 bool get(std::string& value) const
379 {
380 const char* str;
381 size_t size;
382 if (get(str, size))
383 {
384 value.assign(str, size);
385 return true;
386 }
387 return false;
388 }
389
390 bool get(void*& value) const
391 {
392 if (is_data())
393 {
394 value = lua_touserdata(script_.state_, index);
395 return true;
396 }
397 return false;
398 }
399
400 template <class T>
401 bool get(std::vector<T>& array) const
402 {
403 if (!is_table()) return false;
404
405 array.clear();
406
407 slot value = script_[-1];
408 int realIndex = positiveIndex();
409
410 bool done = false;
411 for (int i = 1; !done; ++i)
412 {
413 lua_rawgeti(script_.state_, realIndex, i);
414
415 T v;
416 if (value.get(v)) array.push_back(v);
417 else done = true;
418
419 script_.pop();
420 }
421
422 return true;
423 }
424
425 template <class T>
426 bool get(std::map<std::string,T>& dictionary) const
427 {
428 if (!is_table()) return false;
429
430 dictionary.clear();
431
432 slot key = script_[-2];
433 slot value = script_[-1];
434 int realIndex = positiveIndex();
435
436 script_.push_nil();
437 while (lua_next(script_.state_, realIndex) != 0)
438 {
439 std::string k;
440 if (!key.is_number() && key.get(k))
441 {
442 T v;
443 if (value.get(v)) dictionary[k] = v;
444 }
445 script_.pop();
446 }
447 script_.pop();
448
449 return true;
450 }
451
452 /**
453 * Get the value of a field from the table.
454 */
455
456 template <class T, class V>
457 bool get(T& value, V field) const
458 {
459 bool ret = push_field(field).get(value);
460 script_.pop();
461 return ret;
462 }
463
464
465 template <class T, class V>
466 void set_field(T field, V value)
467 {
468 script_.push(field);
469 script_.push(value);
470 set_field();
471 }
472
473 void set_field()
474 {
475 lua_settable(script_.state_, index);
476 }
477
478
479 template <class T>
480 void set_field(const std::string& field, T value)
481 {
482 set_field(field.c_str(), value);
483 }
484 template <class T>
485 void set_field(const char* field, T value)
486 {
487 script_.push(value);
488 lua_setfield(script_.state_, index, field);
489 }
490
491
492 /**
493 * This set method, as opposed to the others, sets the value of the
494 * actual slot. The others set table values.
495 */
496 template <class T>
497 void set(T value)
498 {
499 script_.push(value);
500 replace();
501 }
502
503 void set()
504 {
505 replace();
506 }
507
508
509 /**
510 * Replace this value with the value at the top of the stack.
511 */
512
513 void replace()
514 {
515 lua_replace(script_.state_, index);
516 }
517
518 void remove()
519 {
520 lua_remove(script_.state_, index);
521 }
522
523 void pop()
524 {
525 // removes this slot, taking with it everything above it
526 script_.pop(script_.stack_size() - index + 1);
527 }
528
529 /**
530 * Inserts the top-most value on the stack at position index,
531 * shifting other values as needed.
532 */
533
534 void insert_top_here()
535 {
536 lua_insert(script_.state_, index);
537 }
538
539
540 /**
541 * Copy the value and push the copy to the stack.
542 */
543
544 slot push_copy() const
545 {
546 lua_pushvalue(script_.state_, index);
547 return script_.top();
548 }
549
550 slot push_metatable() const
551 {
552 lua_getmetatable(script_.state_, index);
553 return script_.top();
554 }
555
556 slot push_environment() const
557 {
558 lua_getfenv(script_.state_, index);
559 return script_.top();
560 }
561
562
563 slot push_field() const
564 {
565 lua_gettable(script_.state_, index);
566 return script_.top();
567 }
568
569 template <class T>
570 slot push_field(T index) const
571 {
572 script_.push(index);
573 return push_field();
574 }
575
576 slot push_field(const std::string& name) const
577 {
578 return push_field(name.c_str());
579 }
580 slot push_field(const char* name) const
581 {
582 lua_getfield(script_.state_, index, name);
583 return script_.top();
584 }
585
586
587 class script& script()
588 {
589 return script_;
590 }
591
592 const class script& script() const
593 {
594 return script_;
595 }
596
597
598 private:
599
600 class script& script_;
601 };
602
603
604 script() :
605 state_(0)
606 {
607 reset();
608 }
609
610 ~script()
611 {
612 destroy();
613 }
614
615
616 static script_ptr alloc()
617 {
618 return script_ptr(new script);
619 }
620
621 void reset()
622 {
623 if (state_) destroy();
624 state_ = luaL_newstate();
625 registry().set_field("Script_hh_Object", (void*)this);
626 }
627
628
629 void import_standard_libraries()
630 {
631 luaL_openlibs(state_);
632 }
633
634 void import_base_library()
635 {
636 lua_pushcfunction(state_, luaopen_base);
637 push(LUA_COLIBNAME);
638 call(1, 0);
639 }
640
641 void import_package_library()
642 {
643 lua_pushcfunction(state_, luaopen_package);
644 push(LUA_LOADLIBNAME);
645 call(1, 0);
646 }
647
648 void import_string_library()
649 {
650 lua_pushcfunction(state_, luaopen_string);
651 push(LUA_STRLIBNAME);
652 call(1, 0);
653 }
654
655 void import_table_library()
656 {
657 lua_pushcfunction(state_, luaopen_table);
658 push(LUA_TABLIBNAME);
659 call(1, 0);
660 }
661
662 void import_math_library()
663 {
664 lua_pushcfunction(state_, luaopen_math);
665 push(LUA_MATHLIBNAME);
666 call(1, 0);
667 }
668
669 void import_io_library()
670 {
671 lua_pushcfunction(state_, luaopen_io);
672 push(LUA_IOLIBNAME);
673 call(1, 0);
674 }
675
676 void import_os_library()
677 {
678 lua_pushcfunction(state_, luaopen_os);
679 push(LUA_OSLIBNAME);
680 call(1, 0);
681 }
682
683 void import_debug_library()
684 {
685 lua_pushcfunction(state_, luaopen_debug);
686 push(LUA_DBLIBNAME);
687 call(1, 0);
688 }
689
690
691 void import_function(const std::string& name, const function& function)
692 {
693 push(function);
694 lua_setglobal(state_, name.c_str());
695 }
696
697 status do_string(const std::string& commands)
698 {
699 return status(luaL_dostring(state_, commands.c_str()));
700 }
701
702 status do_file(const std::string& file)
703 {
704 return status(luaL_dofile(state_, file.c_str()));
705 }
706
707
708 /**
709 * Thread-handling methods.
710 */
711
712 script push_new_thread()
713 {
714 return script(state_);
715 }
716
717 void push_thread()
718 {
719 lua_pushthread(state_);
720 }
721
722 status resume(int nargs)
723 {
724 return status(lua_resume(state_, nargs));
725 }
726
727 status getStatus() const
728 {
729 return status(lua_status(state_));
730 }
731
732 int yield(int results)
733 {
734 return lua_yield(state_, results);
735 }
736
737 bool is_main_thread() const
738 {
739 return is_main_thread_;
740 }
741
742
743 /**
744 * Throw an error with the value at the top of the stack. This method
745 * never returns because it does a long jump. Consequently,
746 * constructed C++ objects which exist on the stack between the
747 * current frame and some lua function will not be destructed. That's
748 * not a problem for objects that only exist on the stack, but any
749 * objects that allocate memory on the heap (such as containers or
750 * strings) will leak. Therefore, you should only call this method
751 * after cleaning up such objects.
752 */
753
754 void raise()
755 {
756 lua_error(state_);
757 }
758
759
760 /**
761 * Get significant values.
762 */
763
764 slot globals() const
765 {
766 return slot(*this, globals_index);
767 }
768
769 slot registry() const
770 {
771 return slot(*this, registry_index);
772 }
773
774 slot environment() const
775 {
776 return slot(*this, environment_index);
777 }
778
779 slot top() const
780 {
781 return slot(*this, stack_size());
782 }
783
784
785 /**
786 * Set the size of the stack.
787 * \param size The stack size.
788 */
789 void stack_size(int size)
790 {
791 lua_settop(state_, size);
792 }
793
794 /**
795 * Get the size of the stack; this is also the index of the top-most
796 * value.
797 * \return The stack size.
798 */
799 int stack_size() const
800 {
801 return lua_gettop(state_);
802 }
803
804 /**
805 * Clear the stack, setting its size to zero.
806 */
807 void clear_stack()
808 {
809 stack_size(0);
810 }
811
812
813 /**
814 * Makes sure there is at least extra more places on the stack.
815 * Returns false if space couldn't be created. Just like with the
816 * regular Lua API, you are responsible to make sure the stack is big
817 * enough to hold whatever you want to push on it. This is usually
818 * only an issue if you're pushing stuff in a loop.
819 */
820 bool checkStack(int extra)
821 {
822 return (bool)lua_checkstack(state_, extra);
823 }
824
825
826 /**
827 * Concatenates the top-most n slots on the stack.
828 */
829 void concatenate(int n = 2)
830 {
831 lua_concat(state_, n);
832 }
833
834
835 /**
836 * Push some values onto the stack.
837 */
838 template <class T>
839 slot push(T value)
840 {
841 lua_pushinteger(state_, lua_Integer(value));
842 return top();
843 }
844
845 slot push(bool value)
846 {
847 lua_pushboolean(state_, int(value));
848 return top();
849 }
850
851 slot push(float value)
852 {
853 lua_pushnumber(state_, (lua_Number)value);
854 return top();
855 }
856 slot push(double value)
857 {
858 lua_pushnumber(state_, (lua_Number)value);
859 return top();
860 }
861
862 slot push(const std::string& value)
863 {
864 lua_pushlstring(state_, value.c_str(), value.length());
865 return top();
866 }
867 slot push(const char* value)
868 {
869 lua_pushstring(state_, value);
870 return top();
871 }
872 slot push(const char* value, size_t length)
873 {
874 lua_pushlstring(state_, value, length);
875 return top();
876 }
877
878 slot push(const function& function)
879 {
880 functions_.push_back(function);
881 lua_pushlightuserdata(state_, (void*)&functions_.back());
882 lua_pushcclosure(state_, dispatch_call, 1);
883 return top();
884 }
885
886 slot push(void* data)
887 {
888 lua_pushlightuserdata(state_, data);
889 return top();
890 }
891
892 slot push_nil()
893 {
894 lua_pushnil(state_);
895 return top();
896 }
897
898 slot push_from_thread(script& thread, int n)
899 {
900 lua_xmove(thread.state_, state_, n);
901 return top();
902 }
903
904 slot push_code(const std::string& file, status& result)
905 {
906 result = status(luaL_loadfile(state_, file.c_str()));
907 return top();
908 }
909
910 slot push_code(const std::string& name,
911 const char* buffer,
912 size_t size,
913 status& result)
914 {
915 result = status(luaL_loadbuffer(state_,
916 buffer, size, name.c_str()));
917 return top();
918 }
919
920 slot push_new_data(void*& data, size_t size)
921 {
922 data = lua_newuserdata(state_, size);
923 return top();
924 }
925
926 slot push_new_table(int narr = 0, int nrec = 0)
927 {
928 lua_createtable(state_, narr, nrec);
929 return top();
930 }
931
932
933 /**
934 * Call a function on the stack. The correct procedure is to push a
935 * function onto the stack followed by nargs arguments. This method
936 * will pop them off upon return, leaving up to nresults return values
937 * (default is any number of return values, depending on the callee).
938 */
939
940 status call(int nargs = 0, int nresults = LUA_MULTRET)
941 {
942 return status(lua_pcall(state_, nargs, nresults, 0));
943 }
944
945
946 /**
947 * Pops n values from the top of the stack.
948 */
949
950 void pop(int n = 1)
951 {
952 lua_pop(state_, n);
953 }
954
955
956 /**
957 * Index into the stack to get a slot.
958 */
959
960 slot operator [] (int index) const
961 {
962 return slot(*this, index);
963 }
964
965
966 /**
967 * Control over the garbage collection process.
968 */
969
970 void collect_all()
971 {
972 lua_gc(state_, LUA_GCCOLLECT, 0);
973 }
974
975 void stop_collector()
976 {
977 lua_gc(state_, LUA_GCSTOP, 0);
978 }
979
980 void restart_collector()
981 {
982 lua_gc(state_, LUA_GCRESTART, 0);
983 }
984
985 int memory_used() const
986 {
987 // in kilobytes
988 return lua_gc(state_, LUA_GCCOUNT, 0);
989 }
990
991 void collect(int step)
992 {
993 lua_gc(state_, LUA_GCSTEP, step);
994 }
995
996 void tune_collector(int pause, int step)
997 {
998 lua_gc(state_, LUA_GCSETPAUSE, pause);
999 lua_gc(state_, LUA_GCSETSTEPMUL, step);
1000 }
1001
1002
1003 private:
1004
1005 script(lua_State* state) :
1006 state_(lua_newthread(state)),
1007 is_main_thread_(false) {}
1008
1009 static int dispatch_call(lua_State* state)
1010 {
1011 const function* function = (const script::function*)lua_touserdata(state,
1012 lua_upvalueindex(1));
1013
1014 lua_getfield(state, LUA_REGISTRYINDEX, "Script_hh_Object");
1015 script* script = (moof::script*)lua_touserdata(state, -1);
1016 lua_pop(state, 1);
1017
1018 return (*function)(*script);
1019 }
1020
1021 void destroy()
1022 {
1023 if (is_main_thread_) lua_close(state_);
1024 }
1025
1026 lua_State* state_;
1027 bool is_main_thread_;
1028 std::list<function> functions_;
1029 };
1030
1031
1032 inline std::ostream& operator << (std::ostream& stream,
1033 const script::slot& slot)
1034 {
1035 std::string str;
1036 bool boolean;
1037
1038 if (slot.get(str))
1039 {
1040 stream << str;
1041 }
1042 else if (slot.get(boolean))
1043 {
1044 if (boolean) stream << "true";
1045 else stream << "false";
1046 }
1047 else if (slot.is_nil())
1048 {
1049 stream << "nil";
1050 }
1051 else
1052 {
1053 stream << slot.type_name() << " (" << slot.id() << ")";
1054 }
1055
1056 return stream;
1057 }
1058
1059
1060 } // namespace moof
1061
1062 #endif // _MOOF_SCRIPT_HH_
1063
This page took 0.0825360000000001 seconds and 4 git commands to generate.