The Battle for Wesnoth  1.17.0-dev
lua_formula_bridge.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2017 - 2021
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
16 
17 #include "game_board.hpp"
19 #include "scripting/lua_unit.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 #include "units/unit.hpp"
30 
31 static const char formulaKey[] = "formula";
32 
33 using namespace wfl;
34 
37 
40  int table_i;
41 public:
42  lua_callable(lua_State* L, int i) : mState(L), table_i(lua_absindex(L,i)) {}
43  variant get_value(const std::string& key) const {
44  if(key == "__list") {
45  std::vector<variant> values;
46  std::size_t n = lua_rawlen(mState, table_i);
47  if(n == 0) {
48  return variant();
49  }
50  for(std::size_t i = 1; i <= n; i++) {
51  lua_pushinteger(mState, i);
52  lua_gettable(mState, table_i);
53  values.push_back(luaW_tofaivariant(mState, -1));
54  }
55  return variant(values);
56  } else if(key == "__map") {
57  std::map<variant,variant> values;
58  for(lua_pushnil(mState); lua_next(mState, table_i); lua_pop(mState, 1)) {
59  values[luaW_tofaivariant(mState, -2)] = luaW_tofaivariant(mState, -1);
60  }
61  return variant(values);
62  }
63  lua_pushlstring(mState, key.c_str(), key.size());
64  lua_gettable(mState, table_i);
65  variant result = luaW_tofaivariant(mState, -1);
66  lua_pop(mState, 1);
67  return result;
68  }
69  void get_inputs(formula_input_vector& inputs) const {
70  add_input(inputs, "__list");
71  add_input(inputs, "__map");
72  for(lua_pushnil(mState); lua_next(mState, table_i); lua_pop(mState,1)) {
73  lua_pushvalue(mState, -2);
74  bool is_valid_key = (lua_type(mState, -1) == LUA_TSTRING) && !lua_isnumber(mState, -1);
75  lua_pop(mState, 1);
76  if(is_valid_key) {
77  std::string key = lua_tostring(mState, -2);
78  if(key.find_first_not_of(formula::id_chars) != std::string::npos) {
79  add_input(inputs, key);
80  }
81  }
82  }
83  }
84  int do_compare(const formula_callable* other) const {
85  const lua_callable* lua = dynamic_cast<const lua_callable*>(other);
86  if(lua == nullptr) {
87  return formula_callable::do_compare(other);
88  }
89  if(mState == lua->mState) { // Which should always be the case, but let's be safe here
90  if(lua_compare(mState, table_i, lua->table_i, LUA_OPEQ)) {
91  return 0;
92  }
93  int top = lua_gettop(mState);
94  if(lua_getmetatable(mState, table_i)) {
95  lua_getfield(mState, -1, "__lt");
96  if(!lua_isnoneornil(mState, -1)) {
97  if(lua_getmetatable(mState, lua->table_i)) {
98  lua_getfield(mState, -1, "__lt");
99  if(!lua_isnoneornil(mState, -1)) {
100  lua_settop(mState, top);
101  return lua_compare(mState, table_i, lua->table_i, LUA_OPLT) ? -1 : 1;
102  }
103  if(lua_compare(mState, -4, -2, LUA_OPEQ)) {
104  lua_settop(mState, top);
105  return 0;
106  }
107  const void* lhs = lua_topointer(mState, -4);
108  const void* rhs = lua_topointer(mState, -2);
109  lua_settop(mState, top);
110  return lhs < rhs ? -1 : (lhs > rhs ? 1 : 0);
111  }
112  }
113  }
114  lua_settop(mState, top);
115  return lua_topointer(mState, -2) < lua_topointer(mState, -1) ? -1 : 1;
116  }
117  return mState < lua->mState ? -1 : 1;
118  }
119 };
120 
122  if(val.is_int()) {
123  lua_pushinteger(L, val.as_int());
124  } else if(val.is_decimal()) {
125  lua_pushnumber(L, val.as_decimal() / 1000.0);
126  } else if(val.is_string()) {
127  const std::string result_string = val.as_string();
128  lua_pushlstring(L, result_string.c_str(), result_string.size());
129  } else if(val.is_list()) {
130  lua_newtable(L);
131  for(const variant& v : val.as_list()) {
132  lua_pushinteger(L, lua_rawlen(L, -1) + 1);
133  luaW_pushfaivariant(L, v);
134  lua_settable(L, -3);
135  }
136  } else if(val.is_map()) {
137  typedef std::map<variant,variant>::value_type kv_type;
138  lua_newtable(L);
139  for(const kv_type& v : val.as_map()) {
140  luaW_pushfaivariant(L, v.first);
141  luaW_pushfaivariant(L, v.second);
142  lua_settable(L, -3);
143  }
144  } else if(val.is_callable()) {
145  // First try a few special cases
146  if(auto u_ref = val.try_convert<unit_callable>()) {
147  const unit& u = u_ref->get_unit();
149  if(&*un_it == &u) {
151  } else {
152  luaW_pushunit(L, u.side(), u.underlying_id());
153  }
154  } else if(auto loc_ref = val.try_convert<location_callable>()) {
155  luaW_pushlocation(L, loc_ref->loc());
156  } else {
157  // If those fail, convert generically to a map
158  auto obj = val.as_callable();
159  formula_input_vector inputs;
160  obj->get_inputs(inputs);
161  lua_newtable(L);
162  for(const formula_input& attr : inputs) {
163  if(attr.access == FORMULA_WRITE_ONLY) {
164  continue;
165  }
166  lua_pushstring(L, attr.name.c_str());
167  luaW_pushfaivariant(L, obj->query_value(attr.name));
168  lua_settable(L, -3);
169  }
170  }
171  } else if(val.is_null()) {
172  lua_pushnil(L);
173  }
174 }
175 
177  switch(lua_type(L, i)) {
178  case LUA_TBOOLEAN:
179  return variant(lua_tointeger(L, i));
180  case LUA_TNUMBER:
182  case LUA_TSTRING:
183  return variant(lua_tostring(L, i));
184  case LUA_TTABLE:
185  return variant(std::make_shared<lua_callable>(L, i));
186  case LUA_TUSERDATA:
187  static t_string tstr;
188  static vconfig vcfg = vconfig::unconstructed_vconfig();
189  static map_location loc;
190  if(luaW_totstring(L, i, tstr)) {
191  return variant(tstr.str());
192  } else if(luaW_tovconfig(L, i, vcfg)) {
193  return variant(std::make_shared<config_callable>(vcfg.get_parsed_config()));
194  } else if(unit* u = luaW_tounit(L, i)) {
195  return variant(std::make_shared<unit_callable>(*u));
196  } else if(luaW_tolocation(L, i, loc)) {
197  return variant(std::make_shared<location_callable>(loc));
198  }
199  break;
200  }
201  return variant();
202 }
203 
204 /**
205  * Evaluates a formula in the formula engine.
206  * - Arg 1: Formula string.
207  * - Arg 2: optional context; can be a unit or a Lua table.
208  * - Ret 1: Result of the formula.
209  */
211 {
212  bool need_delete = false;
213  fwrapper* form;
214  if(void* ud = luaL_testudata(L, 1, formulaKey)) {
215  form = static_cast<fwrapper*>(ud);
216  } else {
217  need_delete = true;
218  form = new fwrapper(luaL_checkstring(L, 1));
219  }
220  std::shared_ptr<formula_callable> context, fallback;
221  if(unit* u = luaW_tounit(L, 2)) {
222  context.reset(new unit_callable(*u));
223  } else if(lua_istable(L, 2)) {
224  context.reset(new lua_callable(L, 2));
225  } else {
226  context.reset(new map_formula_callable);
227  }
228  variant result = form->evaluate(*context);
229  luaW_pushfaivariant(L, result);
230  if(need_delete) {
231  delete form;
232  }
233  return 1;
234 }
235 
237 {
238  if(!lua_isstring(L, 1)) {
239  luaW_type_error(L, 1, "string");
240  }
241  new(L) fwrapper(lua_tostring(L, 1));
243  return 1;
244 }
245 
247  : formula_ptr(new formula(code, functions))
248 {
249 }
250 
252 {
253  if(formula_ptr) {
254  return formula_ptr->str();
255  }
256  return "";
257 }
258 
260 {
261  if(formula_ptr) {
262  return formula_ptr->evaluate(variables, fdb);
263  }
264  return variant();
265 }
266 
268 {
270  form->~fwrapper();
271  return 0;
272 }
273 
275 {
277  const std::string str = form->str();
278  lua_pushlstring(L, str.c_str(), str.size());
279  return 1;
280 }
281 
283 {
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");
293 
294  return "Adding formula metatable...\n";
295 }
#define lua_isnoneornil(L, n)
Definition: lua.h:379
#define lua_pushcfunction(L, f)
Definition: lua.h:370
lua_unit * luaW_pushunit(lua_State *L, Args... args)
Definition: lua_unit.hpp:115
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:917
virtual const unit_map & units() const override
Definition: game_board.hpp:111
LUA_API void lua_settop(lua_State *L, int idx)
Definition: lapi.cpp:173
LUA_API int lua_type(lua_State *L, int idx)
Definition: lapi.cpp:260
bool is_map() const
Definition: variant.hpp:67
This class represents a single unit of a specific type.
Definition: unit.hpp:120
#define LUA_TUSERDATA
Definition: lua.h:72
int luaW_type_error(lua_State *L, int narg, const char *tname)
int as_int() const
Definition: variant.cpp:294
static int impl_formula_tostring(lua_State *L)
bool is_string() const
Definition: variant.hpp:66
LUA_API int lua_gettop(lua_State *L)
Definition: lapi.cpp:168
bool is_callable() const
Definition: variant.hpp:64
LUA_API int lua_gettable(lua_State *L, int idx)
Definition: lapi.cpp:640
LUA_API void lua_settable(lua_State *L, int idx)
Definition: lapi.cpp:821
void get_inputs(formula_input_vector &inputs) const
#define lua_tointeger(L, i)
Definition: lua.h:362
std::vector< formula_input > formula_input_vector
const std::map< variant, variant > & as_map() const
Definition: variant.cpp:333
#define lua_tonumber(L, i)
Definition: lua.h:361
LUALIB_API void luaL_setmetatable(lua_State *L, const char *tname)
Definition: lauxlib.cpp:324
LUA_API int lua_absindex(lua_State *L, int idx)
Definition: lapi.cpp:161
bool is_list() const
Definition: variant.hpp:65
#define lua_pop(L, n)
Definition: lua.h:364
const std::vector< variant > & as_list() const
Definition: variant.cpp:327
bool is_decimal() const
Definition: variant.hpp:63
bool is_int() const
Definition: variant.hpp:62
const std::string & as_string() const
Definition: variant.cpp:321
#define LUA_OPLT
Definition: lua.h:223
int intf_eval_formula(lua_State *)
Evaluates a formula in the formula engine.
#define LUA_TSTRING
Definition: lua.h:69
LUA_API int lua_compare(lua_State *L, int index1, int index2, int op)
Definition: lapi.cpp:327
int as_decimal() const
Returns variant&#39;s internal representation of decimal number: ie, 1.234 is represented as 1234...
Definition: variant.cpp:303
fwrapper(const std::string &code, wfl::function_symbol_table *functions=nullptr)
LUA_API int lua_isstring(lua_State *L, int idx)
Definition: lapi.cpp:292
variant luaW_tofaivariant(lua_State *L, int i)
unit * luaW_tounit(lua_State *L, int index, bool only_on_map)
Converts a Lua value to a unit pointer.
Definition: lua_unit.cpp:142
variant get_value(const std::string &key) const
game_board * gameboard
Definition: resources.cpp:20
wfl::variant evaluate(const wfl::formula_callable &variables, wfl::formula_debugger *fdb=nullptr) const
LUA_API const char * lua_pushlstring(lua_State *L, const char *s, size_t len)
Definition: lapi.cpp:502
#define LUA_TNUMBER
Definition: lua.h:68
LUA_API int lua_getmetatable(lua_State *L, int objindex)
Definition: lapi.cpp:741
config get_parsed_config() const
Definition: variable.cpp:174
LUALIB_API void * luaL_testudata(lua_State *L, int ud, const char *tname)
Definition: lauxlib.cpp:330
int intf_compile_formula(lua_State *)
#define lua_newtable(L)
Definition: lua.h:366
LUA_API void lua_pushnil(lua_State *L)
Definition: lapi.cpp:473
lua_State * mState
LUA_API void lua_pushnumber(lua_State *L, lua_Number n)
Definition: lapi.cpp:481
Encapsulates the map of the game.
Definition: location.hpp:37
static int impl_formula_collect(lua_State *L)
const_formula_callable_ptr as_callable() const
Definition: variant.hpp:82
unit_iterator find(std::size_t id)
Definition: map.cpp:308
bool luaW_totstring(lua_State *L, int index, t_string &str)
Converts a scalar to a translatable string.
Definition: lua_common.cpp:601
LUALIB_API int luaL_newmetatable(lua_State *L, const char *tname)
Definition: lauxlib.cpp:311
LUA_API void * lua_touserdata(lua_State *L, int idx)
Definition: lapi.cpp:432
std::size_t i
Definition: function.cpp:940
std::string register_metatables(lua_State *)
#define lua_tostring(L, i)
Definition: lua.h:386
LUA_API void lua_pushvalue(lua_State *L, int idx)
Definition: lapi.cpp:246
LUA_API int lua_isnumber(lua_State *L, int idx)
Definition: lapi.cpp:285
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:150
lua_callable(lua_State *L, int i)
static const char *const id_chars
Definition: formula.hpp:74
LUA_API lua_Unsigned lua_rawlen(lua_State *L, int idx)
Definition: lapi.cpp:402
bool is_null() const
Functions to test the type of the internal value.
Definition: variant.hpp:61
std::shared_ptr< T > try_convert() const
Definition: variant.hpp:89
LUA_API const void * lua_topointer(lua_State *L, int idx)
Definition: lapi.cpp:451
#define lua_istable(L, n)
Definition: lua.h:373
Definition: contexts.hpp:43
static const char formulaKey[]
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:714
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1345
A variable-expanding proxy for the config class.
Definition: variable.hpp:42
int side() const
The side this unit belongs to.
Definition: unit.hpp:333
virtual int do_compare(const formula_callable *callable) const
Definition: callable.hpp:145
const std::string & str() const
Definition: tstring.hpp:190
LUA_API int lua_getfield(lua_State *L, int idx, const char *k)
Definition: lapi.cpp:655
#define LUA_TTABLE
Definition: lua.h:70
static map_location::DIRECTION n
LUA_API void lua_pushinteger(lua_State *L, lua_Integer n)
Definition: lapi.cpp:489
LUA_API const char * lua_pushstring(lua_State *L, const char *s)
Definition: lapi.cpp:514
LUA_API void lua_setfield(lua_State *L, int idx, const char *k)
Definition: lapi.cpp:837
std::size_t underlying_id() const
This unit&#39;s unique internal ID.
Definition: unit.hpp:382
#define LUA_TBOOLEAN
Definition: lua.h:66
LUA_API int lua_next(lua_State *L, int idx)
Definition: lapi.cpp:1220
int do_compare(const formula_callable *other) const
#define LUA_OPEQ
Definition: lua.h:222
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:725
#define luaL_checkstring(L, n)
Definition: lauxlib.h:138
std::shared_ptr< formula > formula_ptr
Definition: formula_fwd.hpp:21