// Copyright (C) 2012-2013 ChaosForge / Kornel Kisielewicz // http://chaosforge.org/ // // This file is part of NV Libraries. // For conditions of distribution and use, see copyright notice in nv.hh #include "nv/lua/lua_state.hh" #include "nv/lib/lua.hh" #include "nv/logging.hh" #include "nv/string.hh" using namespace nv; // The following should be moved to lua_raw std::string lua_typecontent( lua_State* L, int idx ) { switch ( lua_type( L, idx ) ) { case LUA_TNONE : return "NONE"; case LUA_TNIL : return "NIL"; case LUA_TBOOLEAN : return lua_toboolean( L, idx ) == 0 ? "false" : "true"; case LUA_TLIGHTUSERDATA : return nv::to_string( uint64( lua_touserdata( L, idx ) ) ); case LUA_TNUMBER : return nv::to_string( lua_tonumber( L, idx ) ); case LUA_TSTRING : return lua_tostring( L, idx ); case LUA_TTABLE : return "TABLE"; case LUA_TFUNCTION : return "FUNCTION"; case LUA_TUSERDATA : return nv::to_string( uint64( lua_touserdata( L, idx ) ) ); case LUA_TTHREAD : return "THREAD"; default : return "UNKNOWN!"; } } // ----- lua::stack_guard::stack_guard( lua::state* L ) : L(L), m_level( lua_gettop(L->L) ) { } lua::stack_guard::~stack_guard() { lua_settop( L->L, m_level ); } lua::state::state( bool is_main /*= false*/ ) { m_owner = true; L = luaL_newstate( ); lua_pushcfunction(L, luaopen_base); lua_pushliteral(L, LUA_TABLIBNAME); lua_call(L, 1, 0); if (is_main) { NV_ASSERT( this == get(), "lua_state : another main state created!" ); stack_guard guard( this ); luaopen_base( L ); luaopen_string( L ); luaopen_table( L ); luaopen_math( L ); } NV_LOG( nv::LOG_TRACE, is_main ? "Main Lua state created" : "Secondary Lua state created"); } int lua::state::load_string( const std::string& code, const std::string& name ) { NV_LOG( nv::LOG_TRACE, "Loading Lua string '" << name << "'"); return luaL_loadbuffer( L, code.c_str(), code.length(), name.c_str() ); } int lua::state::load_stream( std::istream& stream, const std::string& name ) { NV_LOG( nv::LOG_NOTICE, "Loading Lua stream '" << name << "'"); return load_string( std::string( (std::istreambuf_iterator(stream)), std::istreambuf_iterator()), name ); } int lua::state::load_file( const std::string& filename ) { NV_LOG( nv::LOG_NOTICE, "Loading Lua file '" << filename << "'"); return luaL_loadfile( L, filename.c_str() ); } bool lua::state::do_string( const std::string& code, const std::string& name ) { lua::stack_guard( this ); int result = load_string(code,name); if (result) { NV_LOG( nv::LOG_WARNING, "Failed to load string " << name << ": " << lua_tostring(L, -1)); return false; } return do_current( name ) == 0; } bool lua::state::do_stream( std::istream& stream, const std::string& name ) { lua::stack_guard( this ); int result = load_stream(stream,name); if (result) { NV_LOG( nv::LOG_WARNING, "Failed to open stream " << name << ": " << lua_tostring(L, -1)); return false; } return do_current( name ) == 0; } bool lua::state::do_file( const std::string& filename ) { lua::stack_guard( this ); int result = load_file(filename); if (result) { NV_LOG( nv::LOG_WARNING, "Failed to open file " << filename << ": " << lua_tostring(L, -1)); return false; } return do_current( filename ) == 0; } int lua::state::do_current( const std::string& name ) { int result = lua_pcall(L, 0, 0, 0); if (result) { NV_LOG( nv::LOG_WARNING, "Failed to run script " << name << ": " << lua_tostring(L, -1)); lua_pop( L, 1 ); } return result; } lua::state::~state() { if (m_owner) { lua_close( L ); } } lua::state* lua::state::get() { static lua::state main_state(true); return &main_state; } bool lua::state::push( const std::string& path, bool global ) { size_t point = path.find('.'); if (point == std::string::npos) { if (global) { lua_getglobal( L, path.c_str() ); } else { lua_getfield( L, -1, path.c_str() ); } return !lua_isnil( L, -1 ); } size_t idx = 0; size_t start = 0; while( point != std::string::npos ) { if (idx == 0) { if (global) { lua_getglobal( L, path.substr(start,point-start).c_str() ); } else { lua_getfield( L, -1, path.substr(start,point-start).c_str() ); } } else { if ( lua_istable( L, -1 ) ) { lua_pushstring( L, path.substr(start,point-start).c_str() ); lua_gettable( L, -2 ); lua_insert( L, -2 ); lua_pop( L, 1 ); } else { lua_pop(L, 1); lua_pushnil(L); return false; } } start = point+1; point = path.find( '.', start ); } return true; } int lua::state::get_stack_size() { return lua_gettop( L ); } lua::table_guard::table_guard( lua::state* lstate, const std::string& table, bool global ) : L(lstate), m_guard(lstate) { L->push( table, global ); } lua::table_guard::table_guard( lua::state* lstate, const std::string& table, int index, bool global ) : L(lstate), m_guard(lstate) { L->push( table, global ); lua_rawgeti( L->L, -1, index ); } lua::table_guard::table_guard( lua::state* lstate, const std::string& table, const std::string& index, bool global /*= true */ ) : L(lstate), m_guard(lstate) { L->push( table, global ); lua_pushstring( L->L, index.c_str() ); lua_rawget( L->L, -2 ); } lua::table_guard::table_guard( const table_guard& parent, const std::string& index ) : L( parent.L ), m_guard( parent.L ) { lua_getfield( L->L, -1, index.c_str() ); } lua::table_guard::table_guard( const table_guard& parent, int index ) : L( parent.L ), m_guard( parent.L ) { lua_rawgeti( L->L, -1, index ); } bool lua::table_guard::has_field( const string& element ) { lua_getfield( L->L, -1, element.c_str() ); bool result = lua_isnil( L->L, -1 ); lua_pop( L->L, 1 ); return result; } string lua::table_guard::get_string( const string& element, const string& defval /*= "" */ ) { lua_getfield( L->L, -1, element.c_str() ); string result( ( lua_type( L->L, -1 ) == LUA_TSTRING ) ? lua_tostring( L->L, -1 ) : defval ); lua_pop( L->L, 1 ); return result; } char lua::table_guard::get_char( const string& element, char defval /*= "" */ ) { lua_getfield( L->L, -1, element.c_str() ); char result = ( lua_type( L->L, -1 ) == LUA_TSTRING && lua_rawlen( L->L, -1 ) > 0 ) ? lua_tostring( L->L, -1 )[0] : defval; lua_pop( L->L, 1 ); return result; } int lua::table_guard::get_integer( const string& element, int defval /*= "" */ ) { lua_getfield( L->L, -1, element.c_str() ); int result = lua_type( L->L, -1 ) == LUA_TNUMBER ? lua_tointeger( L->L, -1 ) : defval; lua_pop( L->L, 1 ); return result; } double lua::table_guard::get_double( const string& element, double defval /*= "" */ ) { lua_getfield( L->L, -1, element.c_str() ); double result = lua_type( L->L, -1 ) == LUA_TNUMBER ? lua_tonumber( L->L, -1 ) : defval; lua_pop( L->L, 1 ); return result; } bool lua::table_guard::get_boolean( const string& element, bool defval /*= "" */ ) { lua_getfield( L->L, -1, element.c_str() ); bool result = lua_type( L->L, -1 ) == LUA_TBOOLEAN ? lua_toboolean( L->L, -1 ) != 0 : defval; lua_pop( L->L, 1 ); return result; } void lua::state::log_stack() { int top = lua_gettop(L); NV_LOG( LOG_DEBUG, "Stack dump (" << top << ")"); for ( int i = 0; i < top; ++i ) { NV_LOG( LOG_DEBUG, "#" << i+1 << " - " << lua_typename(L, i+1) << " = " << lua_typecontent(L, i+1) ); } } lua_State* lua::state::get_raw() { return L; }