]> Dogcows Code - chaz/yoink/blob - src/Moof/Script.hh
simplified win32 installer build script
[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 <iostream>
42 #include <list>
43 #include <map>
44 #include <string>
45 #include <vector>
46
47 #include <boost/bind.hpp>
48 #include <boost/function.hpp>
49 #include <boost/shared_ptr.hpp>
50
51 #include <lua.hpp>
52
53 #include <Moof/Log.hh>
54
55
56 namespace Mf {
57
58
59 class Script;
60 typedef boost::shared_ptr<Script> ScriptP;
61
62
63 class Script
64 {
65 public:
66
67 typedef boost::function<int(Script&)> Function;
68
69 enum Type
70 {
71 NONE = LUA_TNONE,
72 NIL = LUA_TNIL,
73 BOOLEAN = LUA_TBOOLEAN,
74 LIGHTUSERDATA = LUA_TLIGHTUSERDATA,
75 NUMBER = LUA_TNUMBER,
76 STRING = LUA_TSTRING,
77 TABLE = LUA_TTABLE,
78 FUNCTION = LUA_TFUNCTION,
79 USERDATA = LUA_TUSERDATA,
80 THREAD = LUA_TTHREAD
81 };
82
83 enum Result
84 {
85 SUCCESS = 0,
86 YIELD = LUA_YIELD,
87 RUNTIME_ERROR = LUA_ERRRUN,
88 SYNTAX_ERROR = LUA_ERRSYNTAX,
89 MEMORY_ERROR = LUA_ERRMEM,
90 HANDLER_ERROR = LUA_ERRERR,
91 FILE_ERROR = LUA_ERRFILE
92 };
93
94 enum PseudoIndex
95 {
96 REGISTRY = LUA_REGISTRYINDEX,
97 ENVIRONMENT = LUA_ENVIRONINDEX,
98 GLOBALS = LUA_GLOBALSINDEX
99 };
100
101 /**
102 * This is the most prominent abstraction on top of the standard Lua API.
103 * A Slot object represents a value on the stack. More specifically, it
104 * represents a position on the stack. The distinction is only important
105 * when objects are moved around on the stack or if the Slot represents a
106 * negative index on the stack (the value of which will change as things are
107 * pushed onto and popped from the stack).
108 */
109
110 struct Slot
111 {
112 /**
113 * You have direct access to the index of the value on the stack being
114 * represented.
115 */
116
117 int index;
118
119
120 /**
121 * A default-constructed Slot is invalid until a valid Slot is assigned
122 * to it. The only method that should be called on such a Slot is
123 * isValid(), otherwise chaos may ensue. In this case, the Slot will be
124 * invalid even if index is manually changed to a valid index. You have
125 * to index the script itself to get a valid Slot.
126 */
127 Slot(lua_State* s = 0, int i = 0) :
128 index(i),
129 mState(s) {}
130
131 /**
132 * A copied value presently points to the same value, except the real
133 * index is used. That means that if a value that refers to a frame
134 * referenced from the top of the stack will have its normalized index
135 * copied into the new value object.
136 */
137
138 Slot(const Slot& copy) :
139 index(copy.getRealIndex()),
140 mState(copy.mState) {}
141
142
143 // check the type of the value
144 bool isBoolean() const { return (bool)lua_isboolean(mState, index); }
145 bool isFunction() const { return (bool)lua_isfunction(mState, index); }
146 bool isNil() const { return (bool)lua_isnil(mState, index); }
147 bool isNone() const { return (bool)lua_isnone(mState, index); }
148 bool isValid() const { return mState != 0 && !isNone(); }
149 bool isNoneOrNil() const { return (bool)lua_isnoneornil(mState, index); }
150 bool isNumber() const { return (bool)lua_isnumber(mState, index); }
151 bool isString() const { return (bool)lua_isstring(mState, index); }
152 bool isTable() const { return (bool)lua_istable(mState, index); }
153 bool isThread() const { return (bool)lua_isthread(mState, index); }
154 bool isData() const { return (bool)lua_isuserdata(mState, index); }
155 bool isLightData() const { return (bool)lua_islightuserdata(mState, index); }
156
157 /**
158 * Check the value and throw an error if its the wrong type. There's a
159 * little caveat: This method never returns because it does a long jump.
160 * Consequently, constructed C++ objects which exist on the stack
161 * between the current frame and some lua function will not be
162 * destructed. That's not a problem for objects that only exist on the
163 * stack, but any objects that allocate memory on the heap (such as
164 * containers or strings) will leak. Therefore, you should only call
165 * this method after cleaning up such objects. The best thing to do for
166 * defining functions is to simply check all the parameters at the
167 * get-go before any C++ objects are even constructed.
168 */
169
170 void requireType(Type type) const
171 {
172 if (type != getType())
173 {
174 luaL_typerror(mState, index, lua_typename(mState, type));
175 }
176 }
177
178 void throwError(const char* error)
179 {
180 luaL_argerror(mState, index, error);
181 }
182
183
184 Slot& requireBoolean()
185 {
186 if (!isBoolean()) luaL_typerror(mState, index, "boolean");
187 return *this;
188 }
189 Slot& requireNumber()
190 {
191 if (!isNumber()) luaL_typerror(mState, index, "number");
192 return *this;
193 }
194 Slot& requireString()
195 {
196 if (!isString()) luaL_typerror(mState, index, "string");
197 return *this;
198 }
199 Slot& requireTable()
200 {
201 if (!isTable()) luaL_typerror(mState, index, "table");
202 return *this;
203 }
204 Slot& requireFunction()
205 {
206 if (!isFunction()) luaL_typerror(mState, index, "function");
207 return *this;
208 }
209 Slot& requireData()
210 {
211 if (!isData()) luaL_typerror(mState, index, "data");
212 return *this;
213 }
214 Slot& requireNil()
215 {
216 if (!isNil()) luaL_typerror(mState, index, "nil");
217 return *this;
218 }
219 Slot& requireThread()
220 {
221 if (!isThread()) luaL_typerror(mState, index, "thread");
222 return *this;
223 }
224
225
226 /**
227 * Get the type of the value.
228 */
229
230 Type getType() const
231 {
232 return (Type)lua_type(mState, index);
233 }
234
235 /**
236 * Get the name of the type of the value as a string.
237 */
238
239 std::string getTypeName() const
240 {
241 return std::string(luaL_typename(mState, index));
242 }
243
244
245 /**
246 * Get the length of the value according to the definition given by Lua.
247 */
248
249 size_t getLength() const
250 {
251 return lua_objlen(mState, index);
252 }
253
254 int getRealIndex() const
255 {
256 if (index < 0) return lua_gettop(mState) + 1 + index;
257 else return index;
258 }
259
260
261 /**
262 * Get a pointer value (for userdata, tables, threads, and functions).
263 */
264
265 const void* getIdentifier() const
266 {
267 return lua_topointer(mState, index);
268 }
269
270
271 bool operator == (const Slot& rhs) const
272 {
273 return (bool)lua_equal(mState, index, rhs.index);
274 }
275 bool operator != (const Slot& rhs) const
276 {
277 return !(*this == rhs);
278 }
279 bool operator < (const Slot& rhs) const
280 {
281 return (bool)lua_lessthan(mState, index, rhs.index);
282 }
283 bool operator <= (const Slot& rhs) const
284 {
285 return *this < rhs || *this == rhs;
286 }
287 bool operator > (const Slot& rhs) const
288 {
289 return !(*this <= rhs);
290 }
291 bool operator >= (const Slot& rhs) const
292 {
293 return !(*this < rhs);
294 }
295 operator bool () const
296 {
297 return (bool)lua_toboolean(mState, index);
298 }
299
300 Slot& operator = (const Slot& rhs)
301 {
302 rhs.pushCopy();
303 replaceWithTop();
304 return *this;
305 }
306
307
308 /**
309 * Convert the underlying value to a C++ type.
310 */
311
312 template <typename T>
313 bool get(T& value) const
314 {
315 if (isNumber())
316 {
317 value = (T)lua_tointeger(mState, index);
318 return true;
319 }
320 return false;
321 }
322
323 bool get(float& value) const
324 {
325 if (isNumber())
326 {
327 value = (float)lua_tonumber(mState, index);
328 return true;
329 }
330 return false;
331 }
332 bool get(double& value) const
333 {
334 if (isNumber())
335 {
336 value = (double)lua_tonumber(mState, index);
337 return true;
338 }
339 return false;
340 }
341
342 bool get(bool& value) const
343 {
344 if (isBoolean())
345 {
346 value = (bool)lua_toboolean(mState, index);
347 return true;
348 }
349 return false;
350 }
351
352 bool get(std::string& value) const
353 {
354 if (isString())
355 {
356 size_t size;
357 const char* str = lua_tolstring(mState, index, &size);
358 value.assign(str, size);
359 return true;
360 }
361 return false;
362 }
363
364 template <typename T>
365 bool get(std::vector<T>& array) const
366 {
367 if (!isTable()) return false;
368
369 array.clear();
370
371 Slot value(mState, -1);
372 int realIndex = getRealIndex();
373
374 bool done = false;
375 for (int i = 1; !done; ++i)
376 {
377 lua_rawgeti(mState, realIndex, i);
378
379 T v;
380 if (value.get(v)) array.push_back(v);
381 else done = true;
382
383 lua_pop(mState, 1);
384 }
385
386 return true;
387 }
388
389 template <typename T>
390 bool get(std::map<std::string,T>& dictionary) const
391 {
392 if (!isTable()) return false;
393
394 dictionary.clear();
395
396 Slot key(mState, -2);
397 Slot value(mState, -1);
398 int realIndex = getRealIndex();
399
400 lua_pushnil(mState);
401 while (lua_next(mState, realIndex) != 0)
402 {
403 std::string k;
404 if (!key.isNumber() && key.get(k))
405 {
406 T v;
407 if (value.get(v)) dictionary[k] = v;
408 }
409 lua_pop(mState, 1);
410 }
411 lua_pop(mState, 1);
412
413 return true;
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_hh_Object");
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 Result doString(const std::string& commands)
575 {
576 return (Result)luaL_dostring(mState, commands.c_str());
577 }
578
579 Result doFile(const std::string& file)
580 {
581 return (Result)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 Result resume(int nargs)
600 {
601 return (Result)lua_resume(mState, nargs);
602 }
603
604 Result getStatus() const
605 {
606 return (Result)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 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 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 slots on the stack.
696 */
697
698 void concat(int n = 2)
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 Result pushCode(const std::string& filename)
765 {
766 return (Result)luaL_loadfile(mState, filename.c_str());
767 }
768
769 Result pushCode(const std::string& name, const char* buffer, size_t size)
770 {
771 return (Result)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 Result call(int nargs = 0, int nresults = LUA_MULTRET)
793 {
794 return (Result)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 Slot.
810 */
811
812 Slot operator [] (int index) const
813 {
814 return Slot(mState, index);
815 }
816
817
818 /**
819 * Getting and setting fields of a table.
820 */
821
822 void pushField(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_hh_Object");
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 inline std::ostream& operator << (std::ostream& stream,
900 const Script::Slot& slot)
901 {
902 if (slot.isString())
903 {
904 std::string str;
905 slot.get(str);
906 stream << str;
907 }
908 else if (slot.isBoolean())
909 {
910 if (slot) stream << "true";
911 else stream << "false";
912 }
913 else if (slot.isNil())
914 {
915 stream << "nil";
916 }
917 else
918 {
919 stream << slot.getTypeName() << " (" << slot.getIdentifier() << ")";
920 }
921
922 return stream;
923 }
924
925
926 } // namespace Mf
927
928 #endif // _MOOF_SCRIPT_HH_
929
930 /** vim: set ts=4 sw=4 tw=80: *************************************************/
931
This page took 0.072816 seconds and 4 git commands to generate.