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