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