// Copyright (C) 2012-2015 ChaosForge Ltd // http://chaosforge.org/ // // This file is part of Nova libraries. // For conditions of distribution and use, see copying.txt file in root folder. #include "nv/lua/lua_nova.hh" #include "nv/lua/lua_raw.hh" static const char* NV_STATE = "NV_STATE"; static const char* NV_BLUEPRINTS = "NV_BLUEPRINTS"; // static nv::lua::state* nova_get_state( lua_State * L ) // { // int stack = lua_gettop( L ); // nv::lua::state* result = nullptr; // lua_getfield( L, LUA_REGISTRYINDEX, NV_STATE ); // result = static_cast< nv::lua::state* >( lua_touserdata( L, -1 ) ); // lua_settop( L, stack ); // return result; // } // BLUEPRINTS static bool nova_blueprint_exists( lua_State * L, int index ) { lua_getfield( L, LUA_REGISTRYINDEX, NV_BLUEPRINTS ); lua_pushvalue( L, index ); lua_rawget( L, -2 ); bool result = lua_istable( L, -1 ); lua_pop( L, 2 ); return result; } // static void nova_blueprint_push( lua_State * L, int index ) // { // lua_getfield( L, LUA_REGISTRYINDEX, NV_BLUEPRINTS ); // lua_pushvalue( L, index ); // lua_rawget( L, -2 ); // if ( !lua_istable( L, -1 ) ) // { // luaL_error( L, "lua.nova - blueprint \"%s\" doesn't exist!", lua_tolstring( L, index, 0 ) ); // } // lua_replace( L, -2 ); // lua_pop( L, 1 ); // } static int nova_apply_blueprint( lua_State * L ); // forward // returns true if value modified static bool nova_check_type_raw( lua_State * L, int iid, int ifield, int ivalue, int itype ) { iid = nlua_absindex( L, iid ); ifield = nlua_absindex( L, ifield ); ivalue = nlua_absindex( L, ivalue ); itype = nlua_absindex( L, itype ); switch ( lua_type( L, itype ) ) { case LUA_TFUNCTION : lua_pushvalue( L, itype ); // type function lua_pushvalue( L, iid ); // identifier lua_pushvalue( L, ifield ); // field name lua_pushvalue( L, ivalue ); // field value lua_call( L, 3, 1 ); if ( lua_isnoneornil( L, -1 ) ) { lua_pop( L, 1 ); } else { return true; } break; case LUA_TSTRING : if ( lua_type( L, ivalue ) != LUA_TTABLE ) { luaL_error( L, "lua.nova - \"%s.%s\" - type mismatch, table of blueprint \"%s\" expected, %s found!", lua_tolstring( L, iid, 0 ), lua_tolstring( L, ifield, 0 ), lua_tolstring( L, itype, 0 ), lua_typename( L, lua_type( L, ivalue ) ) ); } lua_pushcfunction( L, nova_apply_blueprint ); lua_pushvalue( L, ivalue ); lua_pushvalue( L, itype ); lua_pushvalue( L, iid ); lua_pushliteral( L, "." ); lua_pushvalue( L, ifield ); lua_concat( L, 3 ); lua_call( L, 3, 0 ); break; case LUA_TNUMBER : if (lua_tointeger( L, itype ) != lua_type( L, ivalue ) && lua_tointeger( L, itype ) > 0) { luaL_error( L, "lua.nova - \"%s.%s\" - type mismatch, %s expected, %s found!", lua_tolstring( L, iid, 0 ), lua_tolstring( L, ifield, 0 ), lua_typename( L, (int)lua_tointeger( L, itype ) ), lua_typename( L, lua_type( L, ivalue ) ) ); } break; default : return false; } return false; } static void nova_apply_blueprint_values_raw( lua_State * L, int ibase, int iproto, int iset, int iid ) { ibase = nlua_absindex( L, ibase ); iproto = nlua_absindex( L, iproto ); iset = nlua_absindex( L, iset ); iid = nlua_absindex( L, iid ); lua_pushnil( L ); while ( lua_next( L, iproto ) != 0 ) { // Key -2, Value -1 int ikey = nlua_absindex( L, -2 ); int ivalue = nlua_absindex( L, -1 ); // Base[Key] lua_pushvalue( L, ikey ); lua_rawget( L, ibase ); bool present = !lua_isnil( L, -1 ); lua_pop( L, 1 ); if (lua_type( L, ivalue ) == LUA_TTABLE) { // Value[1] lua_rawgeti( L, ivalue, 1 ); bool mandatory = lua_toboolean( L, -1 ) != 0; bool nested = lua_type( L, -1 ) == LUA_TTABLE; lua_pop( L, 1 ); if (!present) { if (mandatory) { luaL_error( L, "lua.nova - %s has no required field \"%s\"!", lua_tolstring( L, iid, 0 ), lua_tolstring( L, -2, 0 ) ); } else { lua_pushvalue( L, ikey ); // push Key lua_rawgeti( L, ivalue, 3 ); // Value[3] if ( lua_type( L, -1 ) == LUA_TTABLE ) { nlua_shallowcopy( L, -1 ); lua_replace( L, -2 ); } lua_rawset( L, ibase ); // Base[Key] = } } if (nested) { lua_rawgeti( L, ivalue, 1 ); // v[1] lua_pushvalue( L, ikey ); // Key lua_rawget( L, ibase ); // Base[Key] lua_rawget( L, -2 ); // v[1][Base[Key]] lua_replace( L, -2 ); // down to 1 stack if (!lua_isnil( L, -1 )) { nova_apply_blueprint_values_raw( L, ibase, -1, iset, iid ); } lua_pop( L, 1 ); } else if (present) { lua_rawgeti( L, ivalue, 2 ); // Value[2] lua_pushvalue( L, ikey ); // Key lua_rawget( L, ibase ); // Base[Key] if ( nova_check_type_raw( L, iid, ikey, -1, -2 ) ) { lua_pushvalue( L, ikey ); // Key lua_insert( L, -2 ); lua_rawset( L, ibase ); } lua_pop( L, 2 ); } } else { if (present) luaL_error( L, "lua.nova - %s - field \"%s\" cannot be redefined!", lua_tolstring( L, iid, 0 ), lua_tolstring( L, ikey, 0 ) ); // non-table entries get forced copied lua_pushvalue( L, ikey ); // push Key lua_pushvalue( L, ivalue ); // push Value lua_rawset( L, ibase ); // Base[Key] = } lua_pushvalue( L, ikey ); // Key lua_pushnil( L ); lua_rawset( L, iset ); // Set[Key] = nil lua_pop( L, 1 ); } } static int nova_apply_blueprint_values( lua_State * L ) { luaL_checktype( L, 1, LUA_TTABLE ); // base luaL_checktype( L, 2, LUA_TTABLE ); // prototype luaL_checktype( L, 3, LUA_TTABLE ); // set luaL_checktype( L, 4, LUA_TSTRING ); // ident lua_settop( L, 4 ); nova_apply_blueprint_values_raw( L, 1, 2, 3, 4 ); return 0; } static void nova_apply_blueprint_raw( lua_State * L, int ibase, int iproto, int iid ) { ibase = nlua_absindex( L, ibase ); iproto = nlua_absindex( L, iproto ); iid = nlua_absindex( L, iid ); nlua_tokeyset( L, ibase ); int iset = nlua_absindex( L, -1 ); nova_apply_blueprint_values_raw( L, ibase, iproto, iset, iid ); lua_pushnil( L ); while ( lua_next( L, iset ) != 0 ) { luaL_error( L, "lua.nova - %s has unknown field \"%s\"!", lua_tolstring( L, 3, 0 ), lua_tolstring( L, -2, 0 ) ); lua_pop(L, 1); } lua_pop(L, 1); } static int nova_apply_blueprint( lua_State * L ) { luaL_checktype( L, 1, LUA_TTABLE ); // base if ( lua_type( L, 2 ) == LUA_TSTRING ) { lua_getfield( L, LUA_REGISTRYINDEX, NV_BLUEPRINTS ); lua_pushvalue( L, 2 ); lua_rawget( L, -2 ); lua_replace( L, 2 ); } luaL_checktype( L, 2, LUA_TTABLE ); // prototype luaL_checktype( L, 3, LUA_TSTRING ); // ident lua_settop( L, 3 ); nova_apply_blueprint_raw( L, 1, 2, 3 ); return 1; } // stack[1] - table // up[1] - blueprint_id // up[2] - inherit_id static int nova_register_blueprint_closure( lua_State * L ) { luaL_checktype( L, 1, LUA_TTABLE ); lua_getfield( L, LUA_REGISTRYINDEX, NV_BLUEPRINTS ); if ( lua_isnil( L, lua_upvalueindex(2) ) ) { lua_pushvalue( L, lua_upvalueindex(1) ); lua_pushvalue( L, 1 ); lua_rawset( L, -3 ); } else { lua_pushvalue( L, lua_upvalueindex(1) ); lua_pushvalue( L, lua_upvalueindex(2) ); lua_rawget( L, -3 ); nlua_shallowcopy( L, -1 ); lua_replace( L, -2 ); nlua_shallowmerge( L, 1 ); lua_rawset( L, -3 ); } lua_pop( L, 1 ); return 0; } static int nova_register_blueprint( lua_State * L ) { luaL_checktype( L, 1, LUA_TSTRING ); if ( nova_blueprint_exists( L, 1 ) ) { luaL_error( L, "lua.nova - blueprint \"%s\" already registered!", lua_tolstring( L, 1, 0 ) ); } if ( lua_gettop( L ) > 1 ) { luaL_checktype( L, 2, LUA_TSTRING ); if ( !nova_blueprint_exists( L, 2 ) ) { luaL_error( L, "lua.nova - blueprint \"%s\" doesn't exist!", lua_tolstring( L, 2, 0 ) ); } } else { lua_pushnil( L ); } lua_pushcclosure( L, nova_register_blueprint_closure, 2 ); return 1; } static int nova_array_register( lua_State * L ) { // storage.__counter++ lua_pushliteral( L, "__counter" ); lua_pushvalue( L, -1 ); lua_rawget( L, 1 ); int count = 0; if ( !lua_isnil( L, -1 ) ) count = (int)lua_tointeger( L, -1 ); lua_pop( L, 1 ); count++; lua_pushinteger( L, count ); lua_rawset( L, 1 ); // element.nid = __counter lua_pushliteral( L, "nid" ); lua_pushinteger( L, count ); lua_rawset( L, 2 ); // storage[ __counter ] = element lua_pushinteger( L, count ); lua_pushvalue( L, 2 ); lua_rawset( L, 1 ); // return nid lua_pushinteger( L, count ); return 1; } static int nova_register( lua_State * L ) { nova_array_register(L); // element.id = element.id lua_pushliteral( L, "id" ); lua_rawget( L, 2 ); if ( lua_isnil( L, -1 ) ) luaL_error( L, "lua.nova - element without id!" ); // storage[element.id] = element lua_pushvalue( L, -1 ); lua_pushvalue( L, 2 ); lua_rawset( L, 1 ); // core.define( element.id, element.nid ) //LuaSystem.FDefines[ iID ] := lua_tointeger( L, 3 ); // return id return 1; } static int nova_declare( lua_State * L ) { if ( lua_gettop( L ) < 1 ) return 0; if ( lua_gettop( L ) == 1 ) lua_pushboolean( L, false ); lua_settop( L, 2 ); nlua_pushglobaltable( L ); lua_insert( L, 1 ); lua_rawset( L, -3 ); return 0; } static int nova_iif( lua_State * L ) { lua_settop( L, lua_toboolean( L, 1 ) ? 2 : 3 ); return 1; } static int nova_create_seq_function_closure( lua_State * L ) { int fc = (int)lua_tointeger( L, lua_upvalueindex( 1 ) ); int args = lua_gettop( L ); for ( int fi = 1; fi <= fc; fi++ ) { lua_pushvalue( L, lua_upvalueindex( fi + 1 ) ); for ( int i = 1; i <= args; i++ ) lua_pushvalue( L, i ); lua_call( L, args, 0 ); } return 0; } static int nova_create_seq_function( lua_State * L ) { int count = lua_gettop( L ); int upvalues = count; for ( int i = 1; i <= count; i++ ) if ( lua_isnoneornil( L, i ) ) upvalues--; else luaL_checktype( L, i, LUA_TFUNCTION ); if ( upvalues == 0 ) return 0; lua_pushinteger( L, upvalues ); for ( int i = 1; i <= count; i++ ) if ( !lua_isnoneornil( L, i ) ) lua_pushvalue( L, i ); if ( upvalues == 1 ) return 1; lua_pushcclosure( L, nova_create_seq_function_closure, upvalues + 1 ); return 1; } static int nova_type_FLAGS( lua_State * L ) { if ( lua_type( L, 3 ) != LUA_TTABLE ) luaL_error( L, "lua.nova - \"%s.%s\" - type mismatch, flags expected, %s found!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ), lua_typename( L, lua_type( L, 3 ) ) ); nlua_toset( L, 3 ); return 1; } static int nova_type_BLUEPRINT( lua_State * L ) { if ( lua_type( L, 3 ) != LUA_TSTRING ) luaL_error( L, "lua.nova - \"%s.%s\" - type mismatch, blueprint id expected, %s found!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ), lua_typename( L, lua_type( L, 3 ) ) ); if ( !nova_blueprint_exists( L, 3 ) ) luaL_error( L, "lua.nova - \"%s.%s\" - blueprint \"%s\" isn't valid!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ), lua_tolstring( L, 3, 0 ) ); return 0; } static int nova_type_ARRAY_closure( lua_State * L ) { if ( lua_type( L, 3 ) != LUA_TTABLE ) luaL_error( L, "lua.nova - \"%s.%s\" - type mismatch, ARRAY expected, %s found!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ), lua_typename( L, lua_type( L, 3 ) ) ); lua_settop( L, 3 ); lua_pushvalue( L, lua_upvalueindex(1) ); // push type index 4 lua_pushvalue( L, 1 ); lua_pushliteral( L, "." ); lua_pushvalue( L, 2 ); lua_concat( L, 3 ); // new ident index 5 lua_pushnil(L); while ( lua_next( L, 3 ) != 0 ) { // key (index -2), value (index -1) if ( lua_type( L, -2 ) != LUA_TNUMBER ) luaL_error( L, "lua.nova - \"%s.%s\" - type mismatch, ARRAY expected, field found!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ) ); if ( nova_check_type_raw( L, 5, -2, -1, 4 ) ) { lua_pushvalue( L, -3 ); // Key lua_insert( L, -2 ); lua_rawset( L, -3 ); // update Value } lua_pop( L, 1 ); } return 0; } static int nova_type_ARRAY( lua_State * L ) { if ( lua_gettop( L ) != 1 ) luaL_error( L, "lua.nova - Misuse of nova.TARRAY type - usage is core.TARRAY( type )" ); lua_pushcclosure( L, nova_type_ARRAY_closure, 1 ); return 1; } static int nova_type_MAP_closure( lua_State * L ) { if ( lua_type( L, 3 ) != LUA_TTABLE ) luaL_error( L, "lua.nova - \"%s.%s\" - type mismatch, MAP expected, %s found!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ), lua_typename( L, lua_type( L, 3 ) ) ); lua_settop( L, 3 ); lua_pushvalue( L, lua_upvalueindex(1) ); // push key type index 4 lua_pushvalue( L, lua_upvalueindex(2) ); // push value type index 5 lua_pushvalue( L, 1 ); lua_pushliteral( L, "." ); lua_pushvalue( L, 2 ); lua_concat( L, 3 ); // new ident index 6 lua_pushnil(L); while ( lua_next( L, 3 ) != 0 ) { // key (index -2), value (index -1) if ( nova_check_type_raw( L, 6, -2, -2, 4 ) ) luaL_error( L, "lua.nova - \"%s.%s\" - KEY type can't be mutable!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ) ); if ( nova_check_type_raw( L, 6, -2, -1, 5 ) ) { lua_pushvalue( L, -3 ); // Key lua_insert( L, -2 ); lua_rawset( L, -3 ); // update Value } lua_pop( L, 1 ); } return 0; } static int nova_type_MAP( lua_State * L ) { if ( lua_gettop( L ) != 2 ) luaL_error( L, "lua.nova - Misuse of nova.TMAP type - usage is core.TMAP( keytype, valuetype )" ); lua_pushcclosure( L, nova_type_MAP_closure, 2 ); return 1; } static int nova_type_ID_closure( lua_State * L ) { if ( lua_type( L, 3 ) != LUA_TSTRING ) luaL_error( L, "lua.nova - \"%s.%s\" - type mismatch, ID expected, %s found!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ), lua_typename( L, lua_type( L, 3 ) ) ); lua_settop( L, 3 ); lua_pushvalue( L, lua_upvalueindex(1) ); // push type index 4 if ( lua_type( L, -1 ) == LUA_TSTRING ) { nlua_pushglobaltable( L ); lua_pushvalue( L, -2 ); lua_rawget( L, -2 ); lua_replace( L, -2 ); } if ( lua_type( L, -1 ) != LUA_TTABLE ) luaL_error( L, "lua.nova - not a valid storage table in TID!" ); lua_pushvalue( L, 3 ); lua_rawget( L, -2 ); if ( lua_isnoneornil( L, -1 ) ) luaL_error( L, "lua.nova - \"%s.%s\" - valid ID expected!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ) ); return 0; } static int nova_type_ID( lua_State * L ) { if ( lua_gettop( L ) != 1 ) luaL_error( L, "lua.nova - Misuse of nova.TID type - usage is core.TID( storage )" ); lua_pushcclosure( L, nova_type_ID_closure, 1 ); return 1; } static int nova_create_constructor_impl( lua_State * L ) { luaL_checktype( L, 1, LUA_TTABLE ); lua_pushvalue( L, lua_upvalueindex( 1 ) ); // id //ident = vlua_tostring( L, -1 ); //if LuaSystem.Defines.Exists( ident ) then //luaL_error( L, 'Redefinition of id "%s"!', lua_tolstring( L, -1, nil ) ); lua_setfield( L, 1, "id" ); lua_getfield( L, 1, "blueprint" ); if ( lua_isnil( L, -1 ) && (!lua_isnoneornil( L, lua_upvalueindex( 3 ) ) ) ) // blueprint { lua_pushvalue( L, lua_upvalueindex( 3 ) ); lua_replace( L, -2 ); } if ( lua_isnil( L, -1 ) ) // storage.__blueprint { lua_getfield( L, lua_upvalueindex( 2 ), "__blueprint" ); lua_replace( L, -2 ); } if ( !lua_isnil( L, -1 ) ) { lua_getfield( L, lua_upvalueindex( 2 ), "__name" ); // storage.__name lua_pushfstring( L, "%s[%s]", lua_tostring( L, -1 ), lua_tostring( L, lua_upvalueindex( 1 ) ) ); lua_replace( L, -2 ); lua_pushcfunction( L, nova_apply_blueprint ); lua_pushvalue( L, 1 ); lua_pushvalue( L, -4 ); lua_pushvalue( L, -4 ); lua_call( L, 3, 0 ); lua_pop( L, 2 ); } lua_pushcfunction( L, nova_register ); lua_pushvalue( L, lua_upvalueindex( 2 ) ); lua_pushvalue( L, 1 ); lua_call( L, 2, 0 ); if ( !lua_isnoneornil( L, lua_upvalueindex( 4 ) ) ) // constructor { lua_pushvalue( L, lua_upvalueindex( 4 ) ); // constructor lua_pushvalue( L, 1 ); lua_call( L, 1, 0 ); } lua_pushvalue( L, lua_upvalueindex( 1 ) ); // id return 1; } static int nova_create_constructor_closure( lua_State * L ) { luaL_checktype( L, 1, LUA_TSTRING ); if ( lua_gettop( L ) > 1 ) { lua_settop( L, 2 ); luaL_checktype( L, 2, LUA_TSTRING ); lua_pushvalue( L, lua_upvalueindex( 1 ) ); lua_insert( L, -2 ); } else { lua_settop( L, 1 ); lua_pushvalue( L, lua_upvalueindex( 1 ) ); lua_pushvalue( L, lua_upvalueindex( 2 ) ); } lua_pushvalue( L, lua_upvalueindex( 3 ) ); lua_settop( L, 4 ); lua_pushcclosure(L, nova_create_constructor_impl, 4); return 1; } static int nova_create_constructor( lua_State * L ) { if ( lua_type( L, 1 ) == LUA_TSTRING ) { // TODO: Optimzie lua_getglobal( L, lua_tostring( L, 1 ) ); lua_replace( L, 1 ); } luaL_checktype( L, 1, LUA_TTABLE ); if ( !lua_isnoneornil( L, 2 ) ) luaL_checktype( L, 2, LUA_TSTRING ); if ( !lua_isnoneornil( L, 3 ) ) luaL_checktype( L, 3, LUA_TFUNCTION ); lua_settop( L, 3 ); lua_pushcclosure(L, nova_create_constructor_closure, 3); return 1; } static int nova_create_array_constructor_closure( lua_State * L ) { bool blueprint = true; luaL_checktype( L, 1, LUA_TTABLE ); lua_getfield( L, lua_upvalueindex( 1 ), "__blueprint" ); if ( lua_type( L, -1 ) == LUA_TBOOLEAN ) blueprint = lua_toboolean( L, -1 ) != 0; lua_pop( L, 1 ); if ( blueprint ) { lua_getfield( L, 1, "blueprint" ); if ( lua_isnil( L, -1 ) && (!lua_isnoneornil( L, lua_upvalueindex( 2 ) )) ) // blueprint { lua_pushvalue( L, lua_upvalueindex( 2 ) ); lua_replace( L, -2 ); } if ( lua_isnil( L, -1 ) ) // storage.__blueprint { lua_getfield( L, lua_upvalueindex( 1 ), "__blueprint" ); lua_replace( L, -2 ); } if ( !lua_isnil( L, -1 ) && ( lua_type( L, -1 ) != LUA_TBOOLEAN ) ) { lua_getfield( L, lua_upvalueindex( 1 ), "__name" ); // storage.__name lua_pushfstring( L, "%s[%d]", lua_tostring( L, -1 ), lua_objlen(L,lua_upvalueindex( 1 ))+1 ); lua_replace( L, -2 ); lua_pushcfunction( L, nova_apply_blueprint ); lua_pushvalue( L, 1 ); lua_pushvalue( L, -4 ); lua_pushvalue( L, -4 ); lua_call( L, 3, 0 ); lua_pop( L, 2 ); } } lua_pushcfunction( L, nova_array_register ); lua_pushvalue( L, lua_upvalueindex( 1 ) ); lua_pushvalue( L, 1 ); lua_call( L, 2, 1 ); if (!lua_isnoneornil( L, lua_upvalueindex( 3 ) )) // constructor { lua_pushvalue( L, lua_upvalueindex( 3 ) ); // constructor lua_pushvalue( L, 1 ); lua_call( L, 1, 0 ); } return 1; } static int nova_create_array_constructor( lua_State * L ) { if ( lua_type( L, 1 ) == LUA_TSTRING ) { lua_getglobal( L, lua_tostring( L, 1 ) ); lua_replace( L, 1 ); } luaL_checktype( L, 1, LUA_TTABLE ); if ( lua_isnoneornil( L, 2 ) ) { if ( lua_type( L, 2 ) != LUA_TBOOLEAN ) luaL_checktype( L, 2, LUA_TSTRING ); } if ( !lua_isnoneornil( L, 3 ) ) luaL_checktype( L, 3, LUA_TFUNCTION ); lua_settop( L, 3 ); lua_pushcclosure(L, nova_create_array_constructor_closure, 3); return 1; } static int nova_register_storage( lua_State * L ) { bool blueprint = false; bool constr = false; luaL_checktype( L, 1, LUA_TSTRING ); if ( !lua_isnoneornil( L, 2 ) ) { luaL_checktype( L, 2, LUA_TSTRING ); blueprint = true; } if ( !lua_isnoneornil( L, 3 ) ) { luaL_checktype( L, 3, LUA_TFUNCTION ); constr = true; } lua_settop( L, 3 ); nlua_pushglobaltable( L ); lua_pushvalue( L, 1 ); lua_rawget( L, -2 ); if ( !lua_isnil( L, -1 ) ) luaL_error( L, "lua.nova - storage \"%s\" already registered!", lua_tolstring( L, 1, 0 ) ); lua_newtable( L ); // g t lua_pushvalue( L, 1 ); // g t name lua_pushvalue( L, -2 ); // g t name, duplicate table lua_pushvalue( L, 1 ); // g t name, dt, name lua_setfield( L, -2, "__name" ); if ( blueprint ) { lua_pushvalue( L, 2 ); lua_setfield( L, -2, "__blueprint" ); } lua_rawset( L, 4 ); lua_pushcfunction( L, nova_create_constructor ); lua_pushvalue( L, -2 ); // storage if ( blueprint ) lua_pushvalue( L, 2 ); else lua_pushnil( L ); if ( constr ) lua_pushvalue( L, 3 ); else lua_pushnil( L ); lua_call( L, 3, 1 ); return 1; } static int nova_register_array_storage( lua_State * L ) { bool blueprint = false; bool constr = false; luaL_checktype( L, 1, LUA_TSTRING ); if ( !lua_isnoneornil( L, 2 ) ) { if ( lua_type( L, 2 ) != LUA_TBOOLEAN ) luaL_checktype( L, 2, LUA_TSTRING ); blueprint = true; } if ( !lua_isnoneornil( L, 3 ) ) { luaL_checktype( L, 3, LUA_TFUNCTION ); constr = true; } lua_settop( L, 3 ); nlua_pushglobaltable( L ); lua_pushvalue( L, 1 ); lua_rawget( L, -2 ); if ( !lua_isnil( L, -1 ) ) luaL_error( L, "storage \"%s\" already registered!", lua_tolstring( L, lua_upvalueindex(1), 0 ) ); lua_newtable( L ); lua_pushvalue( L, 1 ); // name lua_pushvalue( L, -2 ); // duplicate table lua_pushvalue( L, 1 ); // name lua_setfield( L, -2, "__name" ); if ( blueprint ) { lua_pushvalue( L, 2 ); lua_setfield( L, -2, "__blueprint" ); } lua_rawset( L, 4 ); lua_pushcfunction( L, nova_create_array_constructor ); lua_pushvalue( L, -2 ); // storage if ( blueprint ) lua_pushvalue( L, 2 ); else lua_pushnil( L ); if ( constr ) lua_pushvalue( L, 3 ); else lua_pushnil( L ); lua_call( L, 3, 1 ); return 1; } static const luaL_Reg nova_f[] = { { "TFLAGS", nova_type_FLAGS }, { "TBLUEPRINT", nova_type_BLUEPRINT }, { "TARRAY", nova_type_ARRAY }, { "TMAP", nova_type_MAP }, { "TID", nova_type_ID }, { "iif", nova_iif }, { "register", nova_register }, { "array_register", nova_array_register }, { "declare", nova_declare }, { "create_seq_function", nova_create_seq_function }, { "apply_blueprint", nova_apply_blueprint }, { "apply_blueprint_values", nova_apply_blueprint_values }, { "register_blueprint", nova_register_blueprint }, { "create_constructor", nova_create_constructor }, { "register_storage", nova_register_storage }, { "register_array_storage", nova_register_array_storage }, { "create_array_constructor", nova_create_array_constructor }, {NULL, NULL} }; static int luaopen_nova( lua_State * L ) { lua_createtable( L, 0, 0 ); nlua_register( L, nova_f, -1 ); return 1; } void nv::lua::register_nova( state* a_state ) { lua_State* L = a_state->get_raw(); int stack = lua_gettop( L ); lua_pushlightuserdata( L, a_state ); lua_setfield( L, LUA_REGISTRYINDEX, NV_STATE ); lua_newtable( L ); lua_setfield( L, LUA_REGISTRYINDEX, NV_BLUEPRINTS ); nlua_requiref( L, "nova", luaopen_nova, 1 ); lua_settop( L, stack ); } void nv::lua::register_storage( state* a_state, string_view name, string_view constructor_name ) { // TODO: error checking lua_State* L = a_state->get_raw(); int stack = lua_gettop( L ); // TODO: check if nova is loaded lua_pushcfunction( L, nova_register_storage ); nlua_pushstringview( L, name ); lua_call( L, 1, 1 ); lua_setglobal( L, constructor_name.data() ); lua_settop( L, stack ); }