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