The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
lua_formula_bridge.cpp
Go to the documentation of this file.
1 /*
2  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY.
10 
11  See the COPYING file for more details.
12  */
13 
15 
16 #include "boost/variant/static_visitor.hpp"
17 
19 #include "scripting/lua_api.hpp"
20 #include "scripting/lua_common.hpp"
21 #include "lua/lauxlib.h"
22 #include "lua/lua.h"
23 #include "formula/callable_objects.hpp"
24 #include "formula/formula.hpp"
25 #include "variable.hpp"
26 
27 #include "resources.hpp"
28 #include "units/map.hpp"
29 
32 
33 using namespace game_logic;
34 
37  int table_i;
38 public:
39  lua_callable(lua_State* L, int i) : mState(L), table_i(lua_absindex(L,i)) {}
40  variant get_value(const std::string& key) const {
41  if(key == "__list") {
42  std::vector<variant> values;
43  size_t n = lua_rawlen(mState, table_i);
44  if(n == 0) {
45  return variant();
46  }
47  for(size_t i = 1; i <= n; i++) {
48  lua_pushinteger(mState, i);
49  lua_gettable(mState, table_i);
50  values.push_back(luaW_tofaivariant(mState, -1));
51  }
52  return variant(&values);
53  } else if(key == "__map") {
54  std::map<variant,variant> values;
55  for(lua_pushnil(mState); lua_next(mState, table_i); lua_pop(mState, 1)) {
56  values[luaW_tofaivariant(mState, -2)] = luaW_tofaivariant(mState, -1);
57  }
58  return variant(&values);
59  }
60  lua_pushlstring(mState, key.c_str(), key.size());
61  lua_gettable(mState, table_i);
62  variant result = luaW_tofaivariant(mState, -1);
63  lua_pop(mState, 1);
64  return result;
65  }
66  void get_inputs(std::vector<formula_input>* inputs) const {
67  inputs->push_back(formula_input("__list", FORMULA_READ_ONLY));
68  inputs->push_back(formula_input("__map", FORMULA_READ_ONLY));
69  for(lua_pushnil(mState); lua_next(mState, table_i); lua_pop(mState,1)) {
70  if(lua_isstring(mState, -2) && !lua_isnumber(mState, -2)) {
71  std::string key = lua_tostring(mState, -2);
72  if(key.find_first_not_of(formula::id_chars) != std::string::npos) {
73  inputs->push_back(formula_input(key, FORMULA_READ_ONLY));
74  }
75  }
76  }
77  }
78  int do_compare(const formula_callable* other) const {
79  const lua_callable* lua = dynamic_cast<const lua_callable*>(other);
80  if(lua == nullptr) {
81  return formula_callable::do_compare(other);
82  }
83  if(mState == lua->mState) { // Which should always be the case, but let's be safe here
84  if(lua_compare(mState, table_i, lua->table_i, LUA_OPEQ)) {
85  return 0;
86  }
87  int top = lua_gettop(mState);
88  if(lua_getmetatable(mState, table_i)) {
89  lua_getfield(mState, -1, "__lt");
90  if(!lua_isnoneornil(mState, -1)) {
91  if(lua_getmetatable(mState, lua->table_i)) {
92  lua_getfield(mState, -1, "__lt");
93  if(!lua_isnoneornil(mState, -1)) {
94  lua_settop(mState, top);
95  return lua_compare(mState, table_i, lua->table_i, LUA_OPLT) ? -1 : 1;
96  }
97  if(lua_compare(mState, -4, -2, LUA_OPEQ)) {
98  lua_settop(mState, top);
99  return 0;
100  }
101  const void* lhs = lua_topointer(mState, -4);
102  const void* rhs = lua_topointer(mState, -2);
103  lua_settop(mState, top);
104  return lhs < rhs ? -1 : (lhs > rhs ? 1 : 0);
105  }
106  }
107  }
108  lua_settop(mState, top);
109  return lua_topointer(mState, -2) < lua_topointer(mState, -1) ? -1 : 1;
110  }
111  return mState < lua->mState ? -1 : 1;
112  }
113 };
114 
116  if(val.is_int()) {
117  lua_pushinteger(L, val.as_int());
118  } else if(val.is_decimal()) {
119  lua_pushnumber(L, val.as_decimal() / 1000.0);
120  } else if(val.is_string()) {
121  const std::string result_string = val.as_string();
122  lua_pushlstring(L, result_string.c_str(), result_string.size());
123  } else if(val.is_list()) {
124  lua_newtable(L);
125  for(const variant& v : val.as_list()) {
126  lua_pushinteger(L, lua_rawlen(L, -1) + 1);
128  lua_settable(L, -3);
129  }
130  } else if(val.is_map()) {
131  typedef std::map<variant,variant>::value_type kv_type;
132  lua_newtable(L);
133  for(const kv_type& v : val.as_map()) {
134  luaW_pushfaivariant(L, v.first);
135  luaW_pushfaivariant(L, v.second);
136  lua_settable(L, -3);
137  }
138  } else if(val.is_callable()) {
139  // First try a few special cases
140  if(unit_callable* u_ref = val.try_convert<unit_callable>()) {
141  const unit& u = u_ref->get_unit();
143  if(&*un_it == &u) {
144  new(lua_newuserdata(L, sizeof(lua_unit))) lua_unit(u.underlying_id());
145  } else {
146  new(lua_newuserdata(L, sizeof(lua_unit))) lua_unit(u.side(), u.underlying_id());
147  }
150  lua_setmetatable(L, -2);
151  } else if(location_callable* loc_ref = val.try_convert<location_callable>()) {
152  luaW_pushlocation(L, loc_ref->loc());
153  } else {
154  // If those fail, convert generically to a map
155  const formula_callable* obj = val.as_callable();
156  std::vector<formula_input> inputs;
157  obj->get_inputs(&inputs);
158  lua_newtable(L);
159  for(const formula_input& attr : inputs) {
160  if(attr.access == FORMULA_WRITE_ONLY) {
161  continue;
162  }
163  lua_pushstring(L, attr.name.c_str());
164  luaW_pushfaivariant(L, obj->query_value(attr.name));
165  lua_settable(L, -3);
166  }
167  }
168  } else if(val.is_null()) {
169  lua_pushnil(L);
170  }
171 }
172 
174  switch(lua_type(L, i)) {
175  case LUA_TBOOLEAN:
176  return variant(lua_tointeger(L, i));
177  case LUA_TNUMBER:
179  case LUA_TSTRING:
180  return variant(lua_tostring(L, i));
181  case LUA_TTABLE:
182  return variant(new lua_callable(L, i));
183  case LUA_TUSERDATA:
184  static t_string tstr;
185  static vconfig vcfg = vconfig::unconstructed_vconfig();
186  static map_location loc;
187  if(luaW_totstring(L, i, tstr)) {
188  return variant(tstr.str());
189  } else if(luaW_tovconfig(L, i, vcfg)) {
190  return variant(new config_callable(vcfg.get_parsed_config()));
191  } else if(unit* u = luaW_tounit(L, i)) {
192  return variant(new unit_callable(*u));
193  } else if(luaW_tolocation(L, i, loc)) {
194  return variant(new location_callable(loc));
195  }
196  break;
197  }
198  return variant();
199 }
200 
201 /**
202  * Evaluates a formula in the formula engine.
203  * - Arg 1: Formula string.
204  * - Arg 2: optional context; can be a unit or a Lua table.
205  * - Ret 1: Result of the formula.
206  */
208 {
209  bool need_delete = false;
210  fwrapper* form;
211  if(luaW_hasmetatable(L, 1, formulaKey)) {
212  form = static_cast<fwrapper*>(lua_touserdata(L, 1));
213  } else {
214  need_delete = true;
215  form = new fwrapper(luaL_checkstring(L, 1));
216  }
217  boost::shared_ptr<formula_callable> context, fallback;
218  if(unit* u = luaW_tounit(L, 2)) {
219  context.reset(new unit_callable(*u));
220  } else if(lua_istable(L, 2)) {
221  context.reset(new lua_callable(L, 2));
222  } else {
223  context.reset(new map_formula_callable);
224  }
225  variant result = form->evaluate(*context);
226  luaW_pushfaivariant(L, result);
227  if(need_delete) {
228  delete form;
229  }
230  return 1;
231 }
232 
234 {
235  if(!lua_isstring(L, 1)) {
236  luaL_typerror(L, 1, "string");
237  }
238  new(lua_newuserdata(L, sizeof(fwrapper))) fwrapper(lua_tostring(L, 1));
241  lua_setmetatable(L, -2);
242  return 1;
243 }
244 
246  : formula_ptr(new formula(code, functions))
247 {
248 }
249 
251 {
252  if(formula_ptr) {
253  return formula_ptr->str();
254  }
255  return "";
256 }
257 
259 {
260  if(formula_ptr) {
261  return formula_ptr->evaluate(variables, fdb);
262  }
263  return variant();
264 }
265 
267 {
269  form->~fwrapper();
270  return 0;
271 }
272 
274 {
276  const std::string str = form->str();
277  lua_pushlstring(L, str.c_str(), str.size());
278  return 1;
279 }
280 
282 {
284  lua_createtable(L, 0, 4);
286  lua_setfield(L, -2, "__gc");
288  lua_setfield(L, -2, "__tostring");
290  lua_setfield(L, -2, "__call");
291  lua_pushstring(L, "formula");
292  lua_setfield(L, -2, "__metatable");
294 
295  return "Adding formula metatable...\n";
296 }
#define lua_isnoneornil(L, n)
Definition: lua.h:337
LUA_API void lua_pushlightuserdata(lua_State *L, void *p)
Definition: lapi.cpp:579
LUA_API void lua_createtable(lua_State *L, int narray, int nrec)
Definition: lapi.cpp:667
#define lua_pushcfunction(L, f)
Definition: lua.h:328
LUA_API void lua_getfield(lua_State *L, int idx, const char *k)
Definition: lapi.cpp:622
const map_location & get_location() const
Definition: unit.hpp:286
bool luaW_tovconfig(lua_State *L, int index, vconfig &vcfg)
Gets an optional vconfig from either a table or a userdata.
Definition: lua_common.cpp:757
bool is_callable() const
Definition: variant.hpp:100
LUA_API void lua_settop(lua_State *L, int idx)
Definition: lapi.cpp:159
LUA_API int lua_type(lua_State *L, int idx)
Definition: lapi.cpp:243
Definition: unit.hpp:95
size_t underlying_id() const
The unique internal ID of the unit.
Definition: unit.hpp:150
#define LUA_TUSERDATA
Definition: lua.h:84
config get_parsed_config() const
Definition: variable.cpp:131
static int impl_formula_tostring(lua_State *L)
LUA_API int lua_gettop(lua_State *L)
Definition: lapi.cpp:154
const game_logic::formula_callable * as_callable() const
Definition: variant.hpp:101
LUA_API void lua_settable(lua_State *L, int idx)
Definition: lapi.cpp:741
#define lua_tointeger(L, i)
Definition: lua.h:319
GLuint const GLfloat * val
Definition: glew.h:2614
#define lua_tonumber(L, i)
Definition: lua.h:318
const std::map< variant, variant > & as_map() const
Definition: variant.cpp:617
int side() const
Definition: unit.hpp:201
bool is_decimal() const
Definition: variant.hpp:82
luatypekey const getunitKey
Definition: lua_types.cpp:28
LUA_API int lua_absindex(lua_State *L, int idx)
Definition: lapi.cpp:147
LUA_API void * lua_newuserdata(lua_State *L, size_t size)
Definition: lapi.cpp:1169
bool is_null() const
Definition: variant.hpp:80
variant query_value(const std::string &key) const
Definition: callable.hpp:39
#define lua_pop(L, n)
Definition: lua.h:322
GLboolean GLenum GLenum GLvoid * values
Definition: glew.h:3799
unit * luaW_tounit(lua_State *L, int index, bool only_on_map)
Converts a Lua value to a unit pointer.
Definition: lua_api.cpp:183
#define LUA_OPLT
Definition: lua.h:194
int intf_eval_formula(lua_State *)
Evaluates a formula in the formula engine.
#define LUA_TSTRING
Definition: lua.h:81
LUA_API int lua_compare(lua_State *L, int index1, int index2, int op)
Definition: lapi.cpp:310
variant evaluate(const game_logic::formula_callable &variables, game_logic::formula_debugger *fdb=nullptr) const
GLuint64EXT * result
Definition: glew.h:10727
static const char *const id_chars
Definition: formula.hpp:65
LUA_API int lua_isstring(lua_State *L, int idx)
Definition: lapi.cpp:268
variant luaW_tofaivariant(lua_State *L, int i)
bool is_list() const
Definition: variant.hpp:92
int as_int() const
Definition: variant.cpp:558
const GLdouble * v
Definition: glew.h:1359
void get_inputs(std::vector< formula_input > *inputs) const
LUA_API const char * lua_pushlstring(lua_State *L, const char *s, size_t len)
Definition: lapi.cpp:495
#define LUA_TNUMBER
Definition: lua.h:80
LUA_API int lua_getmetatable(lua_State *L, int objindex)
Definition: lapi.cpp:680
GLhandleARB obj
Definition: glew.h:4486
LUA_API int lua_setmetatable(lua_State *L, int objindex)
Definition: lapi.cpp:806
int intf_compile_formula(lua_State *)
#define lua_newtable(L)
Definition: lua.h:324
LUA_API void lua_pushnil(lua_State *L)
Definition: lapi.cpp:459
lua_State * mState
LUA_API void lua_pushnumber(lua_State *L, lua_Number n)
Definition: lapi.cpp:467
Encapsulates the map of the game.
Definition: location.hpp:38
static int impl_formula_collect(lua_State *L)
Storage for a unit, either owned by the Lua code (ptr != 0), a local variable unit (c_ptr != 0)...
Definition: lua_api.hpp:62
bool luaW_totstring(lua_State *L, int index, t_string &str)
Converts a scalar to a translatable string.
Definition: lua_common.cpp:537
LUA_API void * lua_touserdata(lua_State *L, int idx)
Definition: lapi.cpp:421
bool luaW_hasmetatable(lua_State *L, int index, luatypekey key)
Returns true if the metatable of the object is the one found in the registry.
Definition: lua_common.cpp:524
int as_decimal() const
Definition: variant.cpp:565
bool is_map() const
Definition: variant.hpp:83
LUA_API void lua_rawset(lua_State *L, int idx)
Definition: lapi.cpp:764
std::string register_metatables(lua_State *)
const std::string & as_string() const
Definition: variant.cpp:603
luatypekey const formulaKey
Definition: lua_types.cpp:33
size_t i
Definition: function.cpp:1057
#define lua_tostring(L, i)
Definition: lua.h:345
LUA_API int lua_isnumber(lua_State *L, int idx)
Definition: lapi.cpp:261
static vconfig unconstructed_vconfig()
This is just a wrapper for the default constructor; it exists for historical reasons and to make it c...
Definition: variable.cpp:108
lua_callable(lua_State *L, int i)
T * try_convert() const
Definition: variant.hpp:107
virtual int do_compare(const formula_callable *callable) const
Definition: callable.hpp:78
LUA_API void lua_gettable(lua_State *L, int idx)
Definition: lapi.cpp:613
LUA_API size_t lua_rawlen(lua_State *L, int idx)
Definition: lapi.cpp:401
#define LUA_REGISTRYINDEX
Definition: lua.h:39
GLclampd n
Definition: glew.h:5903
LUA_API const void * lua_topointer(lua_State *L, int idx)
Definition: lapi.cpp:437
#define lua_istable(L, n)
Definition: lua.h:331
void luaW_pushlocation(lua_State *L, const map_location &ml)
Converts a map location object to a Lua table pushed at the top of the stack.
Definition: lua_common.cpp:593
A variable-expanding proxy for the config class.
Definition: variable.hpp:36
const std::vector< variant > & as_list() const
Definition: variant.cpp:610
int do_compare(const formula_callable *other) const
variant get_value(const std::string &key) const
unit_iterator find(size_t id)
Definition: map.cpp:285
const std::string & str() const
Definition: tstring.hpp:170
bool is_string() const
Definition: variant.hpp:79
#define LUA_TTABLE
Definition: lua.h:82
LUA_API void lua_pushinteger(lua_State *L, lua_Integer n)
Definition: lapi.cpp:477
fwrapper(const std::string &code, game_logic::function_symbol_table *functions=nullptr)
virtual void get_inputs(std::vector< formula_input > *) const
Definition: callable.hpp:64
LUALIB_API int luaL_typerror(lua_State *L, int narg, const char *tname)
Definition: lauxlib.cpp:176
LUA_API void lua_rawget(lua_State *L, int idx)
Definition: lapi.cpp:633
GLsizei const GLcharARB ** string
Definition: glew.h:4503
unit_map * units
Definition: resources.cpp:35
LUA_API const char * lua_pushstring(lua_State *L, const char *s)
Definition: lapi.cpp:507
LUA_API void lua_setfield(lua_State *L, int idx, const char *k)
Definition: lapi.cpp:752
#define LUA_TBOOLEAN
Definition: lua.h:78
LUA_API int lua_next(lua_State *L, int idx)
Definition: lapi.cpp:1108
#define LUA_OPEQ
Definition: lua.h:193
void luaW_pushfaivariant(lua_State *L, variant val)
bool luaW_tolocation(lua_State *L, int index, map_location &loc)
Converts an optional table or pair of integers to a map location object.
Definition: lua_common.cpp:606
bool is_int() const
Definition: variant.hpp:81
#define luaL_checkstring(L, n)
Definition: lauxlib.h:115