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