]> Dogcows Code - chaz/yoink/blob - src/Moof/Script.hh
ray-scene intersection
[chaz/yoink] / src / Moof / Script.hh
1
2 /*******************************************************************************
3
4 Copyright (c) 2009, Charles McGarvey
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 * Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 *******************************************************************************/
28
29 #ifndef _MOOF_SCRIPT_HH_
30 #define _MOOF_SCRIPT_HH_
31
32 /**
33 * @file Script.hh
34 * A thin wrapper over Lua. This is not meant as a complicated binding package
35 * between C++ and Lua. It does not try to make the boundary invisible. It
36 * does not hide the concept of the Lua stack, but rather provides that
37 * mechanism with a certain level of abstraction while also providing a cleaner,
38 * more consistent API.
39 */
40
41 #include <list>
42 #include <map>
43 #include <string>
44 #include <vector>
45
46 #include <boost/bind.hpp>
47 #include <boost/function.hpp>
48 #include <boost/shared_ptr.hpp>
49
50 #include <lua.hpp>
51
52 #include <Moof/Log.hh>
53
54
55 namespace Mf {
56
57
58 class Script;
59 typedef boost::shared_ptr<Script> ScriptP;
60
61
62 class Script
63 {
64 public:
65
66 typedef boost::function<int(Script&)> Function;
67
68 enum Type
69 {
70 NONE = LUA_TNONE,
71 NIL = LUA_TNIL,
72 BOOLEAN = LUA_TBOOLEAN,
73 LIGHTUSERDATA = LUA_TLIGHTUSERDATA,
74 NUMBER = LUA_TNUMBER,
75 STRING = LUA_TSTRING,
76 TABLE = LUA_TTABLE,
77 FUNCTION = LUA_TFUNCTION,
78 USERDATA = LUA_TUSERDATA,
79 THREAD = LUA_TTHREAD
80 };
81
82 enum Status
83 {
84 SUCCESS = 0,
85 YIELD = LUA_YIELD,
86 RUNTIME_ERROR = LUA_ERRRUN,
87 SYNTAX_ERROR = LUA_ERRSYNTAX,
88 MEMORY_ERROR = LUA_ERRMEM,
89 HANDLER_ERROR = LUA_ERRERR,
90 FILE_ERROR = LUA_ERRFILE
91 };
92
93 enum PseudoIndex
94 {
95 REGISTRY = LUA_REGISTRYINDEX,
96 ENVIRONMENT = LUA_ENVIRONINDEX,
97 GLOBALS = LUA_GLOBALSINDEX
98 };
99
100 /**
101 * This is the most prominent abstraction on top of the standard Lua API.
102 * A Value object represents a value on the stack. More specifically, it
103 * represents a position on the stack. The distinction is only important
104 * when values are moved around on the stack or if the Value represents a
105 * negative index on the stack (the value of which will change as things are
106 * pushed onto and popped from the stack).
107 */
108
109 struct Value
110 {
111 /**
112 * You have direct access to the index of the value on the stack being
113 * represented.
114 */
115
116 int index;
117
118
119 /**
120 * A default-constructed Value is invalid until a valid Value is
121 * assigned to it. The only method that should be called on such a
122 * Value is isValid(), otherwise chaos may ensue. In this case, the
123 * Value will be invalid even if index is manually changed to a valid
124 * index. You have to index the script itself to get a valid Value.
125 */
126 Value(lua_State* s = 0, int i = 0) :
127 index(i),
128 mState(s) {}
129
130 /**
131 * A copied value presently points to the same value, except the real
132 * index is used. That means that if a value that refers to a frame
133 * referenced from the top of the stack will have its normalized index
134 * copied into the new value object.
135 */
136
137 Value(const Value& copy) :
138 index(copy.getRealIndex()),
139 mState(copy.mState) {}
140
141
142 // check the type of the value
143 bool isBoolean() const { return (bool)lua_isboolean(mState, index); }
144 bool isFunction() const { return (bool)lua_isfunction(mState, index); }
145 bool isNil() const { return (bool)lua_isnil(mState, index); }
146 bool isNone() const { return (bool)lua_isnone(mState, index); }
147 bool isValid() const { return mState != 0 && !isNone(); }
148 bool isNoneOrNil() const { return (bool)lua_isnoneornil(mState, index); }
149 bool isNumber() const { return (bool)lua_isnumber(mState, index); }
150 bool isString() const { return (bool)lua_isstring(mState, index); }
151 bool isTable() const { return (bool)lua_istable(mState, index); }
152 bool isThread() const { return (bool)lua_isthread(mState, index); }
153 bool isData() const { return (bool)lua_isuserdata(mState, index); }
154 bool isLightData() const { return (bool)lua_islightuserdata(mState, index); }
155
156 /**
157 * Check the value and throw an error if its the wrong type. There's a
158 * little caveat: This method never returns because it does a long jump.
159 * Consequently, constructed C++ objects which exist on the stack
160 * between the current frame and some lua function will not be
161 * destructed. That's not a problem for objects that only exist on the
162 * stack, but any objects that allocate memory on the heap (such as
163 * containers or strings) will leak. Therefore, you should only call
164 * this method after cleaning up such objects. The best thing to do for
165 * defining functions is to simply check all the parameters at the
166 * get-go before any C++ objects are even constructed.
167 */
168
169 void requireType(Type type) const
170 {
171 if (type != getType())
172 {
173 luaL_typerror(mState, index, lua_typename(mState, type));
174 }
175 }
176
177 void throwError(const char* error)
178 {
179 luaL_argerror(mState, index, error);
180 }
181
182
183 Value& requireBoolean()
184 {
185 if (!isBoolean()) luaL_typerror(mState, index, "boolean");
186 return *this;
187 }
188 Value& requireNumber()
189 {
190 if (!isNumber()) luaL_typerror(mState, index, "number");
191 return *this;
192 }
193 Value& requireString()
194 {
195 if (!isString()) luaL_typerror(mState, index, "string");
196 return *this;
197 }
198 Value& requireTable()
199 {
200 if (!isTable()) luaL_typerror(mState, index, "table");
201 return *this;
202 }
203 Value& requireFunction()
204 {
205 if (!isFunction()) luaL_typerror(mState, index, "function");
206 return *this;
207 }
208 Value& requireData()
209 {
210 if (!isData()) luaL_typerror(mState, index, "data");
211 return *this;
212 }
213 Value& requireNil()
214 {
215 if (!isNil()) luaL_typerror(mState, index, "nil");
216 return *this;
217 }
218 Value& requireThread()
219 {
220 if (!isThread()) luaL_typerror(mState, index, "thread");
221 return *this;
222 }
223
224
225 /**
226 * Get the type of the value.
227 */
228
229 Type getType() const
230 {
231 return (Type)lua_type(mState, index);
232 }
233
234 /**
235 * Get the name of the type of the value as a string.
236 */
237
238 std::string getTypeName() const
239 {
240 return std::string(luaL_typename(mState, index));
241 }
242
243
244 /**
245 * Get the length of the value according to the definition given by Lua.
246 */
247
248 size_t getLength() const
249 {
250 return lua_objlen(mState, index);
251 }
252
253 int getRealIndex() const
254 {
255 if (index < 0) return lua_gettop(mState) + 1 + index;
256 else return index;
257 }
258
259
260 /**
261 * Get a pointer value (for userdata, tables, threads, and functions).
262 */
263
264 const void* getIdentifier() const
265 {
266 return lua_topointer(mState, index);
267 }
268
269
270 bool operator == (const Value& rhs) const
271 {
272 return (bool)lua_equal(mState, index, rhs.index);
273 }
274 bool operator != (const Value& rhs) const
275 {
276 return !(*this == rhs);
277 }
278 bool operator < (const Value& rhs) const
279 {
280 return (bool)lua_lessthan(mState, index, rhs.index);
281 }
282 bool operator <= (const Value& rhs) const
283 {
284 return *this < rhs || *this == rhs;
285 }
286 bool operator > (const Value& rhs) const
287 {
288 return !(*this <= rhs);
289 }
290 bool operator >= (const Value& rhs) const
291 {
292 return !(*this < rhs);
293 }
294 operator bool () const
295 {
296 return (bool)lua_toboolean(mState, index);
297 }
298
299 Value& operator = (const Value& 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 Value 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 Value key(mState, -2);
396 Value 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 /**
418 * Copy the value and push the copy to the stack.
419 */
420
421 void pushCopy() const
422 {
423 lua_pushvalue(mState, index);
424 }
425
426 /**
427 * Replace this value with the value at the top of the stack.
428 */
429
430 void replaceWithTop()
431 {
432 lua_replace(mState, index);
433 }
434
435 void remove()
436 {
437 lua_remove(mState, index);
438 }
439
440 /**
441 * Inserts the top-most value on the stack at position index, shifting other
442 * values as needed.
443 */
444
445 void insertTopHere()
446 {
447 lua_insert(mState, index);
448 }
449
450
451 void pushMetatable() const
452 {
453 lua_getmetatable(mState, index);
454 }
455
456 void pushField() const
457 {
458 lua_gettable(mState, index);
459 }
460
461 void pushField(const std::string& name) const
462 {
463 lua_getfield(mState, index, name.c_str());
464 }
465
466 void pushField(size_t index) const
467 {
468 lua_pushinteger(mState, lua_Integer(index));
469 pushField();
470 }
471
472
473 private:
474
475 lua_State* mState;
476 };
477
478
479 Script() :
480 mState(0)
481 {
482 reset();
483 }
484
485 ~Script()
486 {
487 destroy();
488 }
489
490
491 static ScriptP alloc()
492 {
493 return ScriptP(new Script);
494 }
495
496 void reset()
497 {
498 if (mState) destroy();
499 mState = luaL_newstate();
500 lua_pushlightuserdata(mState, this);
501 lua_setfield(mState, LUA_REGISTRYINDEX, "_script_obj");
502 }
503
504
505 void importStandardLibraries()
506 {
507 luaL_openlibs(mState);
508 }
509
510 void importBaseLibrary()
511 {
512 lua_pushcfunction(mState, luaopen_base);
513 push(LUA_COLIBNAME);
514 call(1, 0);
515 }
516
517 void importPackageLibrary()
518 {
519 lua_pushcfunction(mState, luaopen_package);
520 push(LUA_LOADLIBNAME);
521 call(1, 0);
522 }
523
524 void importStringLibrary()
525 {
526 lua_pushcfunction(mState, luaopen_string);
527 push(LUA_STRLIBNAME);
528 call(1, 0);
529 }
530
531 void importTableLibrary()
532 {
533 lua_pushcfunction(mState, luaopen_table);
534 push(LUA_TABLIBNAME);
535 call(1, 0);
536 }
537
538 void importMathLibrary()
539 {
540 lua_pushcfunction(mState, luaopen_math);
541 push(LUA_MATHLIBNAME);
542 call(1, 0);
543 }
544
545 void importIoLibrary()
546 {
547 lua_pushcfunction(mState, luaopen_io);
548 push(LUA_IOLIBNAME);
549 call(1, 0);
550 }
551
552 void importOsLibrary()
553 {
554 lua_pushcfunction(mState, luaopen_os);
555 push(LUA_OSLIBNAME);
556 call(1, 0);
557 }
558
559 void importDebugLibrary()
560 {
561 lua_pushcfunction(mState, luaopen_debug);
562 push(LUA_DBLIBNAME);
563 call(1, 0);
564 }
565
566
567 void importFunction(const std::string& name, const Function& function)
568 {
569 push(function);
570 lua_setglobal(mState, name.c_str());
571 }
572
573
574 Status doString(const std::string& commands)
575 {
576 return (Status)luaL_dostring(mState, commands.c_str());
577 }
578
579 Status doFile(const std::string& file)
580 {
581 return (Status)luaL_dofile(mState, file.c_str());
582 }
583
584
585 /**
586 * Thread-handling methods.
587 */
588
589 Script pushNewThread()
590 {
591 return Script(mState);
592 }
593
594 void pushThread()
595 {
596 lua_pushthread(mState);
597 }
598
599 Status resume(int nargs)
600 {
601 return (Status)lua_resume(mState, nargs);
602 }
603
604 Status getStatus() const
605 {
606 return (Status)lua_status(mState);
607 }
608
609 int yield(int results)
610 {
611 return lua_yield(mState, results);
612 }
613
614 bool isMainThread() const
615 {
616 return mIsMainThread;
617 }
618
619
620 /**
621 * Throw an error with the value at the top of the stack. This method never
622 * returns because it does a long jump. Consequently, constructed C++
623 * objects which exist on the stack between the current frame and some lua
624 * function will not be destructed. That's not a problem for objects that
625 * only exist on the stack, but any objects that allocate memory on the heap
626 * (such as containers or strings) will leak. Therefore, you should only
627 * call this method 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 Value getGlobalTable() const
641 {
642 return Value(mState, GLOBALS);
643 }
644
645 Value getRegistryTable() const
646 {
647 return Value(mState, REGISTRY);
648 }
649
650 Value getEnvironmentTable() const
651 {
652 return Value(mState, ENVIRONMENT);
653 }
654
655 Value getTop() const
656 {
657 return Value(mState, lua_gettop(mState));
658 }
659
660 /**
661 * Get the size of the stack; this is also the index of the top-most value.
662 */
663
664 int getSize() const
665 {
666 return lua_gettop(mState);
667 }
668
669 void setSize(int size)
670 {
671 lua_settop(mState, size);
672 }
673
674 void clear()
675 {
676 setSize(0);
677 }
678
679
680 /**
681 * Makes sure there is at least extra more places on the stack. Returns
682 * false if space couldn't be created. Just like with the regular Lua API,
683 * you are responsible to make sure the stack is big enough to hold whatever
684 * you want to push on it. This is usually only an issue if you're pushing
685 * stuff in a loop.
686 */
687
688 bool checkStack(int extra)
689 {
690 return (bool)lua_checkstack(mState, extra);
691 }
692
693
694 /**
695 * Concatenates the top-most n values on the stack.
696 */
697
698 void concat(int n)
699 {
700 lua_concat(mState, n);
701 }
702
703
704 /**
705 * Push some values onto the stack.
706 */
707
708 template <typename T>
709 void push(T value)
710 {
711 lua_pushinteger(mState, lua_Integer(value));
712 }
713
714 void push(bool value)
715 {
716 lua_pushboolean(mState, int(value));
717 }
718
719 void push(float value)
720 {
721 lua_pushnumber(mState, (lua_Number)value);
722 }
723 void push(double value)
724 {
725 lua_pushnumber(mState, (lua_Number)value);
726 }
727
728 void push(const std::string& value)
729 {
730 lua_pushlstring(mState, value.c_str(), value.length());
731 }
732 void push(const char* value)
733 {
734 lua_pushstring(mState, value);
735 }
736 void push(const char* value, size_t length)
737 {
738 lua_pushlstring(mState, value, length);
739 }
740
741 void push(const Function& function)
742 {
743 mFunctions.push_back(function);
744
745 lua_pushlightuserdata(mState, (void*)&mFunctions.back());
746 lua_pushcclosure(mState, dispatchCall, 1);
747 }
748
749 void push(void* data)
750 {
751 lua_pushlightuserdata(mState, data);
752 }
753
754 void pushNil()
755 {
756 lua_pushnil(mState);
757 }
758
759 void pushFromThread(Script& thread, int n)
760 {
761 lua_xmove(thread.mState, mState, n);
762 }
763
764 Status pushCode(const std::string& filename)
765 {
766 return (Status)luaL_loadfile(mState, filename.c_str());
767 }
768
769 Status pushCode(const std::string& name, const char* buffer, size_t size)
770 {
771 return (Status)luaL_loadbuffer(mState, buffer, size, name.c_str());
772 }
773
774 void* pushNewData(size_t size)
775 {
776 return lua_newuserdata(mState, size);
777 }
778
779 void pushNewTable()
780 {
781 lua_newtable(mState);
782 }
783
784
785 /**
786 * Call a function on the stack. The correct procedure is to push a
787 * function onto the stack followed by nargs arguments. This method will
788 * pop them off upon return, leaving up to nresults return values (default
789 * is any number of return values, depending on the callee).
790 */
791
792 Status call(int nargs, int nresults = LUA_MULTRET)
793 {
794 return (Status)lua_pcall(mState, nargs, nresults, 0);
795 }
796
797
798 /**
799 * Pops n values from the top of the stack.
800 */
801
802 void pop(int n = 1)
803 {
804 lua_pop(mState, n);
805 }
806
807
808 /**
809 * Index into the stack to get a Value.
810 */
811
812 Value operator [] (int index) const
813 {
814 return Value(mState, index);
815 }
816
817
818 /**
819 * Getting and setting fields of a table.
820 */
821
822 void get(const std::string& field, int index = GLOBALS) const
823 {
824 lua_getfield(mState, index, field.c_str());
825 }
826
827 void set(const std::string& field, int index = GLOBALS)
828 {
829 lua_setfield(mState, index, field.c_str());
830 }
831
832
833 /**
834 * Control over the garbage collection process.
835 */
836
837 void collectAll()
838 {
839 lua_gc(mState, LUA_GCCOLLECT, 0);
840 }
841
842 void stopCollector()
843 {
844 lua_gc(mState, LUA_GCSTOP, 0);
845 }
846
847 void restartCollector()
848 {
849 lua_gc(mState, LUA_GCRESTART, 0);
850 }
851
852 int getUsedMemory() const
853 {
854 // in kilobytes
855 return lua_gc(mState, LUA_GCCOUNT, 0);
856 }
857
858 void collectStep(int step)
859 {
860 lua_gc(mState, LUA_GCSTEP, step);
861 }
862
863 void tuneCollector(int pause, int step)
864 {
865 lua_gc(mState, LUA_GCSETPAUSE, pause);
866 lua_gc(mState, LUA_GCSETSTEPMUL, step);
867 }
868
869
870 private:
871
872 Script(lua_State* state) :
873 mState(lua_newthread(state)),
874 mIsMainThread(false) {}
875
876 static int dispatchCall(lua_State* state)
877 {
878 const Function* function = (const Function*)lua_touserdata(state,
879 lua_upvalueindex(1));
880
881 lua_getfield(state, LUA_REGISTRYINDEX, "_script_obj");
882 Script* script = (Script*)lua_touserdata(state, -1);
883 lua_pop(state, 1);
884
885 return (*function)(*script);
886 }
887
888 void destroy()
889 {
890 if (mIsMainThread) lua_close(mState);
891 }
892
893 lua_State* mState;
894 bool mIsMainThread;
895 std::list<Function> mFunctions;
896 };
897
898
899 } // namespace Mf
900
901 #endif // _MOOF_SCRIPT_HH_
902
903 /** vim: set ts=4 sw=4 tw=80: *************************************************/
904
This page took 0.072722 seconds and 4 git commands to generate.