9af54402ae74846e88418531d8b679e71595c02d
[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 does not try to make the boundary
19 * invisible. It does not hide the concept of the Lua stack, but rather
20 * provides that mechanism with a certain level of abstraction while also
21 * providing a cleaner, more consistent API.
22 */
23
24 #include <iostream>
25 #include <list>
26 #include <map>
27 #include <string>
28 #include <vector>
29
30 #include <boost/bind.hpp>
31 #include <boost/function.hpp>
32 #include <boost/shared_ptr.hpp>
33 #include <lua.hpp>
34
35 #include <Moof/Log.hh>
36
37
38 namespace Mf {
39
40
41 class Script;
42 typedef boost::shared_ptr<Script> ScriptP;
43
44
45 class Script
46 {
47 public:
48
49 typedef boost::function<int(Script&)> Function;
50
51 enum Type
52 {
53 NONE = LUA_TNONE,
54 NIL = LUA_TNIL,
55 BOOLEAN = LUA_TBOOLEAN,
56 LIGHTUSERDATA = LUA_TLIGHTUSERDATA,
57 NUMBER = LUA_TNUMBER,
58 STRING = LUA_TSTRING,
59 TABLE = LUA_TTABLE,
60 FUNCTION = LUA_TFUNCTION,
61 USERDATA = LUA_TUSERDATA,
62 THREAD = LUA_TTHREAD
63 };
64
65 enum Result
66 {
67 SUCCESS = 0,
68 YIELD = LUA_YIELD,
69 RUNTIME_ERROR = LUA_ERRRUN,
70 SYNTAX_ERROR = LUA_ERRSYNTAX,
71 MEMORY_ERROR = LUA_ERRMEM,
72 HANDLER_ERROR = LUA_ERRERR,
73 FILE_ERROR = LUA_ERRFILE
74 };
75
76 enum PseudoIndex
77 {
78 REGISTRY = LUA_REGISTRYINDEX,
79 ENVIRONMENT = LUA_ENVIRONINDEX,
80 GLOBALS = LUA_GLOBALSINDEX
81 };
82
83 /**
84 * This is the most prominent abstraction on top of the standard Lua
85 * API. A Slot object represents a value on the stack. More
86 * specifically, it represents a position on the stack. The
87 * distinction is only important when objects are moved around on the
88 * stack or if the Slot represents a negative index on the stack (the
89 * value of which will change as things are pushed onto and popped from
90 * the stack).
91 */
92
93 struct Slot
94 {
95 /**
96 * You have direct access to the index of the value on the stack
97 * being represented.
98 */
99
100 int index;
101
102
103 /**
104 * A default-constructed Slot is invalid until a valid Slot is
105 * assigned to it. The only method that should be called on such a
106 * Slot is isValid(), otherwise chaos may ensue. In this case, the
107 * Slot will be invalid even if index is manually changed to a
108 * valid index. You have to index the script itself to get a valid
109 * Slot.
110 */
111 Slot(lua_State* s = 0, int i = 0) :
112 index(i),
113 mState(s) {}
114
115 /**
116 * A copied value presently points to the same value, except the
117 * real index is used. That means that if a value that refers to a
118 * frame referenced from the top of the stack will have its
119 * normalized index copied into the new value object.
120 */
121
122 Slot(const Slot& copy) :
123 index(copy.getRealIndex()),
124 mState(copy.mState) {}
125
126
127 // check the type of the value
128 bool isBoolean() const
129 { return (bool)lua_isboolean(mState, index); }
130 bool isFunction() const
131 { return (bool)lua_isfunction(mState, index); }
132 bool isNil() const
133 { return (bool)lua_isnil(mState, index); }
134 bool isNone() const
135 { return (bool)lua_isnone(mState, index); }
136 bool isValid() const
137 { return mState != 0 && !isNone(); }
138 bool isNoneOrNil() const
139 { return (bool)lua_isnoneornil(mState, index); }
140 bool isNumber() const
141 { return (bool)lua_isnumber(mState, index); }
142 bool isString() const
143 { return (bool)lua_isstring(mState, index); }
144 bool isTable() const
145 { return (bool)lua_istable(mState, index); }
146 bool isThread() const
147 { return (bool)lua_isthread(mState, index); }
148 bool isData() const
149 { return (bool)lua_isuserdata(mState, index); }
150 bool isLightData() const
151 { return (bool)lua_islightuserdata(mState, index); }
152
153 /**
154 * Check the value and throw an error if its the wrong type.
155 * There's a little caveat: This method never returns because it
156 * does a long jump. Consequently, constructed C++ objects which
157 * exist on the stack between the current frame and some lua
158 * function will not be destructed. That's not a problem for
159 * objects that only exist on the stack, but any objects that
160 * allocate memory on the heap (such as containers or strings) will
161 * leak. Therefore, you should only call this method after
162 * cleaning up such objects. The best thing to do for defining
163 * functions is to simply check all the parameters at the get-go
164 * before any C++ objects are even constructed.
165 */
166
167 void requireType(Type type) const
168 {
169 if (type != getType())
170 {
171 luaL_typerror(mState, index, lua_typename(mState, type));
172 }
173 }
174
175 void throwError(const char* error)
176 {
177 luaL_argerror(mState, index, error);
178 }
179
180
181 Slot& requireBoolean()
182 {
183 if (!isBoolean()) luaL_typerror(mState, index, "boolean");
184 return *this;
185 }
186 Slot& requireNumber()
187 {
188 if (!isNumber()) luaL_typerror(mState, index, "number");
189 return *this;
190 }
191 Slot& requireString()
192 {
193 if (!isString()) luaL_typerror(mState, index, "string");
194 return *this;
195 }
196 Slot& requireTable()
197 {
198 if (!isTable()) luaL_typerror(mState, index, "table");
199 return *this;
200 }
201 Slot& requireFunction()
202 {
203 if (!isFunction()) luaL_typerror(mState, index, "function");
204 return *this;
205 }
206 Slot& requireData()
207 {
208 if (!isData()) luaL_typerror(mState, index, "data");
209 return *this;
210 }
211 Slot& requireNil()
212 {
213 if (!isNil()) luaL_typerror(mState, index, "nil");
214 return *this;
215 }
216 Slot& requireThread()
217 {
218 if (!isThread()) luaL_typerror(mState, index, "thread");
219 return *this;
220 }
221
222
223 /**
224 * Get the type of the value.
225 */
226
227 Type getType() const
228 {
229 return (Type)lua_type(mState, index);
230 }
231
232 /**
233 * Get the name of the type of the value as a string.
234 */
235
236 std::string getTypeName() const
237 {
238 return std::string(luaL_typename(mState, index));
239 }
240
241
242 /**
243 * Get the length of the value according to the definition given by
244 * Lua.
245 */
246
247 size_t getLength() const
248 {
249 return lua_objlen(mState, index);
250 }
251
252 int getRealIndex() const
253 {
254 if (index < 0) return lua_gettop(mState) + 1 + index;
255 else return index;
256 }
257
258
259 /**
260 * Get a pointer value (for userdata, tables, threads, and
261 * functions).
262 */
263
264 const void* getIdentifier() const
265 {
266 return lua_topointer(mState, index);
267 }
268
269
270 bool operator == (const Slot& rhs) const
271 {
272 return (bool)lua_equal(mState, index, rhs.index);
273 }
274 bool operator != (const Slot& rhs) const
275 {
276 return !(*this == rhs);
277 }
278 bool operator < (const Slot& rhs) const
279 {
280 return (bool)lua_lessthan(mState, index, rhs.index);
281 }
282 bool operator <= (const Slot& rhs) const
283 {
284 return *this < rhs || *this == rhs;
285 }
286 bool operator > (const Slot& rhs) const
287 {
288 return !(*this <= rhs);
289 }
290 bool operator >= (const Slot& rhs) const
291 {
292 return !(*this < rhs);
293 }
294 operator bool () const
295 {
296 return (bool)lua_toboolean(mState, index);
297 }
298
299 Slot& operator = (const Slot& rhs)
300 {
301 rhs.pushCopy();
302 replaceWithTop();
303 return *this;
304 }
305
306
307 /**
308 * Convert the underlying value to a C++ type.
309 */
310
311 template <typename T>
312 bool get(T& value) const
313 {
314 if (isNumber())
315 {
316 value = (T)lua_tointeger(mState, index);
317 return true;
318 }
319 return false;
320 }
321
322 bool get(float& value) const
323 {
324 if (isNumber())
325 {
326 value = (float)lua_tonumber(mState, index);
327 return true;
328 }
329 return false;
330 }
331 bool get(double& value) const
332 {
333 if (isNumber())
334 {
335 value = (double)lua_tonumber(mState, index);
336 return true;
337 }
338 return false;
339 }
340
341 bool get(bool& value) const
342 {
343 if (isBoolean())
344 {
345 value = (bool)lua_toboolean(mState, index);
346 return true;
347 }
348 return false;
349 }
350
351 bool get(std::string& value) const
352 {
353 if (isString())
354 {
355 size_t size;
356 const char* str = lua_tolstring(mState, index, &size);
357 value.assign(str, size);
358 return true;
359 }
360 return false;
361 }
362
363 template <typename T>
364 bool get(std::vector<T>& array) const
365 {
366 if (!isTable()) return false;
367
368 array.clear();
369
370 Slot value(mState, -1);
371 int realIndex = getRealIndex();
372
373 bool done = false;
374 for (int i = 1; !done; ++i)
375 {
376 lua_rawgeti(mState, realIndex, i);
377
378 T v;
379 if (value.get(v)) array.push_back(v);
380 else done = true;
381
382 lua_pop(mState, 1);
383 }
384
385 return true;
386 }
387
388 template <typename T>
389 bool get(std::map<std::string,T>& dictionary) const
390 {
391 if (!isTable()) return false;
392
393 dictionary.clear();
394
395 Slot key(mState, -2);
396 Slot value(mState, -1);
397 int realIndex = getRealIndex();
398
399 lua_pushnil(mState);
400 while (lua_next(mState, realIndex) != 0)
401 {
402 std::string k;
403 if (!key.isNumber() && key.get(k))
404 {
405 T v;
406 if (value.get(v)) dictionary[k] = v;
407 }
408 lua_pop(mState, 1);
409 }
410 lua_pop(mState, 1);
411
412 return true;
413 }
414
415
416 /**
417 * Copy the value and push the copy to the stack.
418 */
419
420 void pushCopy() const
421 {
422 lua_pushvalue(mState, index);
423 }
424
425 /**
426 * Replace this value with the value at the top of the stack.
427 */
428
429 void replaceWithTop()
430 {
431 lua_replace(mState, index);
432 }
433
434 void remove()
435 {
436 lua_remove(mState, index);
437 }
438
439 /**
440 * Inserts the top-most value on the stack at position index,
441 * shifting other values as needed.
442 */
443
444 void insertTopHere()
445 {
446 lua_insert(mState, index);
447 }
448
449
450 void pushMetatable() const
451 {
452 lua_getmetatable(mState, index);
453 }
454
455 void pushField() const
456 {
457 lua_gettable(mState, index);
458 }
459
460 void pushField(const std::string& name) const
461 {
462 lua_getfield(mState, index, name.c_str());
463 }
464
465 void pushField(size_t index) const
466 {
467 lua_pushinteger(mState, lua_Integer(index));
468 pushField();
469 }
470
471
472 private:
473
474 lua_State* mState;
475 };
476
477
478 Script() :
479 mState(0)
480 {
481 reset();
482 }
483
484 ~Script()
485 {
486 destroy();
487 }
488
489
490 static ScriptP alloc()
491 {
492 return ScriptP(new Script);
493 }
494
495 void reset()
496 {
497 if (mState) destroy();
498 mState = luaL_newstate();
499 lua_pushlightuserdata(mState, this);
500 lua_setfield(mState, LUA_REGISTRYINDEX, "Script_hh_Object");
501 }
502
503
504 void importStandardLibraries()
505 {
506 luaL_openlibs(mState);
507 }
508
509 void importBaseLibrary()
510 {
511 lua_pushcfunction(mState, luaopen_base);
512 push(LUA_COLIBNAME);
513 call(1, 0);
514 }
515
516 void importPackageLibrary()
517 {
518 lua_pushcfunction(mState, luaopen_package);
519 push(LUA_LOADLIBNAME);
520 call(1, 0);
521 }
522
523 void importStringLibrary()
524 {
525 lua_pushcfunction(mState, luaopen_string);
526 push(LUA_STRLIBNAME);
527 call(1, 0);
528 }
529
530 void importTableLibrary()
531 {
532 lua_pushcfunction(mState, luaopen_table);
533 push(LUA_TABLIBNAME);
534 call(1, 0);
535 }
536
537 void importMathLibrary()
538 {
539 lua_pushcfunction(mState, luaopen_math);
540 push(LUA_MATHLIBNAME);
541 call(1, 0);
542 }
543
544 void importIoLibrary()
545 {
546 lua_pushcfunction(mState, luaopen_io);
547 push(LUA_IOLIBNAME);
548 call(1, 0);
549 }
550
551 void importOsLibrary()
552 {
553 lua_pushcfunction(mState, luaopen_os);
554 push(LUA_OSLIBNAME);
555 call(1, 0);
556 }
557
558 void importDebugLibrary()
559 {
560 lua_pushcfunction(mState, luaopen_debug);
561 push(LUA_DBLIBNAME);
562 call(1, 0);
563 }
564
565
566 void importFunction(const std::string& name, const Function& function)
567 {
568 push(function);
569 lua_setglobal(mState, name.c_str());
570 }
571
572
573 Result doString(const std::string& commands)
574 {
575 return (Result)luaL_dostring(mState, commands.c_str());
576 }
577
578 Result doFile(const std::string& file)
579 {
580 return (Result)luaL_dofile(mState, file.c_str());
581 }
582
583
584 /**
585 * Thread-handling methods.
586 */
587
588 Script pushNewThread()
589 {
590 return Script(mState);
591 }
592
593 void pushThread()
594 {
595 lua_pushthread(mState);
596 }
597
598 Result resume(int nargs)
599 {
600 return (Result)lua_resume(mState, nargs);
601 }
602
603 Result getStatus() const
604 {
605 return (Result)lua_status(mState);
606 }
607
608 int yield(int results)
609 {
610 return lua_yield(mState, results);
611 }
612
613 bool isMainThread() const
614 {
615 return mIsMainThread;
616 }
617
618
619 /**
620 * Throw an error with the value at the top of the stack. This method
621 * never returns because it does a long jump. Consequently,
622 * constructed C++ objects which exist on the stack between the
623 * current frame and some lua function will not be destructed. That's
624 * not a problem for objects that only exist on the stack, but any
625 * objects that allocate memory on the heap (such as containers or
626 * strings) will leak. Therefore, you should only call this method
627 * after cleaning up such objects.
628 */
629
630 void throwError()
631 {
632 lua_error(mState);
633 }
634
635
636 /**
637 * Get significant values.
638 */
639
640 Slot getGlobalTable() const
641 {
642 return Slot(mState, GLOBALS);
643 }
644
645 Slot getRegistryTable() const
646 {
647 return Slot(mState, REGISTRY);
648 }
649
650 Slot getEnvironmentTable() const
651 {
652 return Slot(mState, ENVIRONMENT);
653 }
654
655 Slot getTop() const
656 {
657 return Slot(mState, lua_gettop(mState));
658 }
659
660 /**
661 * Get the size of the stack; this is also the index of the top-most
662 * value.
663 */
664
665 int getSize() const
666 {
667 return lua_gettop(mState);
668 }
669
670 void setSize(int size)
671 {
672 lua_settop(mState, size);
673 }
674
675 void clear()
676 {
677 setSize(0);
678 }
679
680
681 /**
682 * Makes sure there is at least extra more places on the stack.
683 * Returns false if space couldn't be created. Just like with the
684 * regular Lua API, you are responsible to make sure the stack is big
685 * enough to hold whatever you want to push on it. This is usually
686 * only an issue if you're pushing stuff in a loop.
687 */
688
689 bool checkStack(int extra)
690 {
691 return (bool)lua_checkstack(mState, extra);
692 }
693
694
695 /**
696 * Concatenates the top-most n slots on the stack.
697 */
698
699 void concat(int n = 2)
700 {
701 lua_concat(mState, n);
702 }
703
704
705 /**
706 * Push some values onto the stack.
707 */
708
709 template <typename T>
710 void push(T value)
711 {
712 lua_pushinteger(mState, lua_Integer(value));
713 }
714
715 void push(bool value)
716 {
717 lua_pushboolean(mState, int(value));
718 }
719
720 void push(float value)
721 {
722 lua_pushnumber(mState, (lua_Number)value);
723 }
724 void push(double value)
725 {
726 lua_pushnumber(mState, (lua_Number)value);
727 }
728
729 void push(const std::string& value)
730 {
731 lua_pushlstring(mState, value.c_str(), value.length());
732 }
733 void push(const char* value)
734 {
735 lua_pushstring(mState, value);
736 }
737 void push(const char* value, size_t length)
738 {
739 lua_pushlstring(mState, value, length);
740 }
741
742 void push(const Function& function)
743 {
744 mFunctions.push_back(function);
745
746 lua_pushlightuserdata(mState, (void*)&mFunctions.back());
747 lua_pushcclosure(mState, dispatchCall, 1);
748 }
749
750 void push(void* data)
751 {
752 lua_pushlightuserdata(mState, data);
753 }
754
755 void pushNil()
756 {
757 lua_pushnil(mState);
758 }
759
760 void pushFromThread(Script& thread, int n)
761 {
762 lua_xmove(thread.mState, mState, n);
763 }
764
765 Result pushCode(const std::string& filename)
766 {
767 return (Result)luaL_loadfile(mState, filename.c_str());
768 }
769
770 Result pushCode(const std::string& name, const char* buffer,
771 size_t size)
772 {
773 return (Result)luaL_loadbuffer(mState, buffer, size, name.c_str());
774 }
775
776 void* pushNewData(size_t size)
777 {
778 return lua_newuserdata(mState, size);
779 }
780
781 void pushNewTable()
782 {
783 lua_newtable(mState);
784 }
785
786
787 /**
788 * Call a function on the stack. The correct procedure is to push a
789 * function onto the stack followed by nargs arguments. This method
790 * will pop them off upon return, leaving up to nresults return values
791 * (default is any number of return values, depending on the callee).
792 */
793
794 Result call(int nargs = 0, int nresults = LUA_MULTRET)
795 {
796 return (Result)lua_pcall(mState, nargs, nresults, 0);
797 }
798
799
800 /**
801 * Pops n values from the top of the stack.
802 */
803
804 void pop(int n = 1)
805 {
806 lua_pop(mState, n);
807 }
808
809
810 /**
811 * Index into the stack to get a Slot.
812 */
813
814 Slot operator [] (int index) const
815 {
816 return Slot(mState, index);
817 }
818
819
820 /**
821 * Getting and setting fields of a table.
822 */
823
824 void pushField(const std::string& field, int index = GLOBALS) const
825 {
826 lua_getfield(mState, index, field.c_str());
827 }
828
829 void set(const std::string& field, int index = GLOBALS)
830 {
831 lua_setfield(mState, index, field.c_str());
832 }
833
834
835 /**
836 * Control over the garbage collection process.
837 */
838
839 void collectAll()
840 {
841 lua_gc(mState, LUA_GCCOLLECT, 0);
842 }
843
844 void stopCollector()
845 {
846 lua_gc(mState, LUA_GCSTOP, 0);
847 }
848
849 void restartCollector()
850 {
851 lua_gc(mState, LUA_GCRESTART, 0);
852 }
853
854 int getUsedMemory() const
855 {
856 // in kilobytes
857 return lua_gc(mState, LUA_GCCOUNT, 0);
858 }
859
860 void collectStep(int step)
861 {
862 lua_gc(mState, LUA_GCSTEP, step);
863 }
864
865 void tuneCollector(int pause, int step)
866 {
867 lua_gc(mState, LUA_GCSETPAUSE, pause);
868 lua_gc(mState, LUA_GCSETSTEPMUL, step);
869 }
870
871
872 private:
873
874 Script(lua_State* state) :
875 mState(lua_newthread(state)),
876 mIsMainThread(false) {}
877
878 static int dispatchCall(lua_State* state)
879 {
880 const Function* function = (const Function*)lua_touserdata(state,
881 lua_upvalueindex(1));
882
883 lua_getfield(state, LUA_REGISTRYINDEX, "Script_hh_Object");
884 Script* script = (Script*)lua_touserdata(state, -1);
885 lua_pop(state, 1);
886
887 return (*function)(*script);
888 }
889
890 void destroy()
891 {
892 if (mIsMainThread) lua_close(mState);
893 }
894
895 lua_State* mState;
896 bool mIsMainThread;
897 std::list<Function> mFunctions;
898 };
899
900
901 inline std::ostream& operator << (std::ostream& stream,
902 const Script::Slot& slot)
903 {
904 if (slot.isString())
905 {
906 std::string str;
907 slot.get(str);
908 stream << str;
909 }
910 else if (slot.isBoolean())
911 {
912 if (slot) stream << "true";
913 else stream << "false";
914 }
915 else if (slot.isNil())
916 {
917 stream << "nil";
918 }
919 else
920 {
921 stream << slot.getTypeName() << " (" << slot.getIdentifier() << ")";
922 }
923
924 return stream;
925 }
926
927
928 } // namespace Mf
929
930 #endif // _MOOF_SCRIPT_HH_
931
This page took 0.066482 seconds and 3 git commands to generate.