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