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