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