The Battle for Wesnoth  1.15.2+dev
lua_wml.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2018 by Chris Beck <render787@gmail.com>
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 
15 #include "scripting/lua_wml.hpp"
17 #include "scripting/lua_common.hpp"
18 
21 #include "serialization/parser.hpp"
23 #include "variable.hpp" // for config_variable_set
24 
25 #include <fstream>
26 
27 #include "lua/lua.h"
28 #include "lua/lauxlib.h"
29 
30 namespace lua_wml {
31 
32 /**
33 * Dumps a wml table or userdata wml object into a pretty string.
34 * - Arg 1: wml table or vconfig userdata
35 * - Ret 1: string
36 */
37 static int intf_wml_tostring(lua_State* L) {
38  const config& arg = luaW_checkconfig(L, 1);
39  std::ostringstream stream;
40  write(stream, arg);
41  lua_pushstring(L, stream.str().c_str());
42  return 1;
43 }
44 
45 /**
46  * Loads a WML file into a config
47  * - Arg 1: WML file path
48  * - Arg 2: (optional) Array of preprocessor defines, or false to skip preprocessing (true is also valid)
49  * - Arg 3: (optional) Path to a schema file for validation (omit for no validation)
50  * - Ret: config
51  */
52 static int intf_load_wml(lua_State* L)
53 {
54  std::string file = luaL_checkstring(L, 1);
55  bool preprocess = true;
56  preproc_map defines_map;
57  if(lua_type(L, 2) == LUA_TBOOLEAN) {
58  preprocess = luaW_toboolean(L, 2);
59  } else if(lua_type(L, 2) == LUA_TTABLE || lua_type(L, 2) == LUA_TUSERDATA) {
60  lua_len(L, 2);
61  int n = lua_tonumber(L, -1);
62  lua_pop(L, 1);
63  for(int i = 0; i < n; i++) {
64  lua_geti(L, 2, i);
65  if(!lua_isstring(L, -1)) {
66  return luaL_argerror(L, 2, "expected bool or array of strings");
67  }
68  std::string define = lua_tostring(L, -1);
69  lua_pop(L, 1);
70  if(!define.empty()) {
71  defines_map.emplace(define, preproc_define(define));
72  }
73  }
74  } else if(!lua_isnoneornil(L, 2)) {
75  return luaL_argerror(L, 2, "expected bool or array of strings");
76  }
77  std::string schema_path = luaL_optstring(L, 3, "");
78  std::shared_ptr<schema_validation::schema_validator> validator;
79  if(!schema_path.empty()) {
80  validator.reset(new schema_validation::schema_validator(filesystem::get_wml_location(schema_path)));
81  validator->set_create_exceptions(false); // Don't crash if there's an error, just go ahead anyway
82  }
83  std::string wml_file = filesystem::get_wml_location(file);
85  config result;
86  if(preprocess) {
87  stream = preprocess_file(wml_file, &defines_map);
88  } else {
89  stream.reset(new std::ifstream(wml_file));
90  }
91  read(result, *stream, validator.get());
92  luaW_pushconfig(L, result);
93  return 1;
94 }
95 
96 /**
97  * Parses a WML string into a config; does not preprocess or validate
98  * - Arg 1: WML string
99  * - Ret: config
100  */
101 static int intf_parse_wml(lua_State* L)
102 {
103  std::string wml = luaL_checkstring(L, 1);
104  std::string schema_path = luaL_optstring(L, 2, "");
105  std::shared_ptr<schema_validation::schema_validator> validator;
106  if(!schema_path.empty()) {
107  validator.reset(new schema_validation::schema_validator(filesystem::get_wml_location(schema_path)));
108  validator->set_create_exceptions(false); // Don't crash if there's an error, just go ahead anyway
109  }
110  config result;
111  read(result, wml, validator.get());
112  luaW_pushconfig(L, result);
113  return 1;
114 }
115 
116 /**
117  * Returns a clone (deep copy) of the passed config, which can be either a normal config or a vconfig
118  * If it is a vconfig, the underlying config is also cloned.
119  * - Arg 1: a config
120  * - Ret: the cloned config
121  */
122 static int intf_clone_wml(lua_State* L)
123 {
124  const vconfig* vcfg = nullptr;
125  const config& cfg = luaW_checkconfig(L, 1, vcfg);
126  if(vcfg) {
127  config clone_underlying = vcfg->get_config();
128  vconfig clone(clone_underlying);
129  luaW_pushvconfig(L, clone);
130  } else {
131  luaW_pushconfig(L, cfg);
132  }
133  return 1;
134 }
135 
136 /**
137 * Interpolates variables into a WML table, including [insert_tag]
138 * Arg 1: WML table to interpolate into
139 * Arg 2: WML table of variables
140 */
142 {
143  config cfg = luaW_checkconfig(L, 1), vars_cfg = luaW_checkconfig(L, 2);
144  config_variable_set vars(vars_cfg);
145  vconfig vcfg(cfg, vars);
147  return 1;
148 }
149 
150 /**
151 * Tests if a WML table matches a filter
152 * Arg 1: table to test
153 * Arg 2: filter
154 */
156 {
157  config cfg = luaW_checkconfig(L, 1);
158  config filter = luaW_checkconfig(L, 2);
159  lua_pushboolean(L, cfg.matches(filter));
160  return 1;
161 }
162 
163 /**
164 * Merges two WML tables
165 * Arg 1: base table
166 * Arg 2: table to merge in
167 */
168 static int intf_wml_merge(lua_State* L)
169 {
170  config base = luaW_checkconfig(L, 1);
171  config merge = luaW_checkconfig(L, 2);
172  const std::string mode = lua_isstring(L, 3) ? luaL_checkstring(L, 3) : "merge";
173  if(mode == "append") {
174  base.merge_attributes(merge);
175  base.append_children(merge);
176  } else {
177  if(mode == "replace") {
178  for(const auto& c : merge.all_children_range()) {
179  base.clear_children(c.key);
180  }
181  } else if(mode != "merge") {
182  return luaL_argerror(L, 3, "invalid merge mode - must be merge, append, or replace");
183  }
184  base.merge_with(merge);
185  }
186  luaW_pushconfig(L, base);
187  return 1;
188 }
189 
190 /**
191 * Computes a diff of two WML tables
192 * Arg 1: left table
193 * Arg 2: right table
194 */
195 static int intf_wml_diff(lua_State* L)
196 {
197  config lhs = luaW_checkconfig(L, 1);
198  config rhs = luaW_checkconfig(L, 2);
199  luaW_pushconfig(L, lhs.get_diff(rhs));
200  return 1;
201 }
202 
203 /**
204 * Applies a diff to a WML table
205 * Arg 1: base table
206 * Arg 2: WML diff
207 */
208 static int intf_wml_patch(lua_State* L)
209 {
210  config base = luaW_checkconfig(L, 1);
211  config patch = luaW_checkconfig(L, 2);
212  base.apply_diff(patch);
213  luaW_pushconfig(L, base);
214  return 1;
215 }
216 
217 /**
218 * Tests if two WML tables are equal (have the same keys and values, same tags, recursively)
219 * Arg 1: left table
220 * Arg 2: right table
221 */
222 static int intf_wml_equal(lua_State* L)
223 {
224  config left = luaW_checkconfig(L, 1);
225  config right = luaW_checkconfig(L, 2);
226  lua_pushboolean(L, left == right);
227  return 1;
228 }
229 
230 /**
231 * Tests if a table represents a valid WML table
232 * Arg 1: table
233 */
234 static int intf_wml_valid(lua_State* L)
235 {
236  config test;
237  if(luaW_toconfig(L, 1, test)) {
238  // The validate_wml call is PROBABLY redundant, but included just in case validation changes and toconfig isn't updated to match
239  lua_pushboolean(L, test.validate_wml());
240  } else lua_pushboolean(L, false);
241  return 1;
242 }
243 
245  auto& lk = lua_kernel_base::get_lua_kernel<lua_kernel_base>(L);
246  lk.add_log("Adding wml module...\n");
247  static luaL_Reg const wml_callbacks[]= {
248  { "load", &intf_load_wml},
249  { "parse", &intf_parse_wml},
250  { "clone", &intf_clone_wml},
251  { "merge", &intf_wml_merge},
252  { "diff", &intf_wml_diff},
253  { "patch", &intf_wml_patch},
254  { "equal", &intf_wml_equal},
255  { "valid", &intf_wml_valid},
256  { "matches_filter", &intf_wml_matches_filter},
257  { "tostring", &intf_wml_tostring},
258  { "interpolate", &intf_wml_interpolate},
259  { nullptr, nullptr },
260  };
261  lua_newtable(L);
262  luaL_setfuncs(L, wml_callbacks, 0);
263  return 1;
264 }
265 
266 }
#define lua_isnoneornil(L, n)
Definition: lua.h:359
static int intf_wml_equal(lua_State *L)
Tests if two WML tables are equal (have the same keys and values, same tags, recursively) Arg 1: left...
Definition: lua_wml.cpp:222
bool matches(const config &filter) const
Definition: config.cpp:1229
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:921
void luaW_pushconfig(lua_State *L, const config &cfg)
Converts a config object to a Lua table pushed at the top of the stack.
Definition: lua_common.cpp:730
void clear_children(T... keys)
Definition: config.hpp:479
LUA_API int lua_type(lua_State *L, int idx)
Definition: lapi.cpp:251
static int intf_wml_interpolate(lua_State *L)
Interpolates variables into a WML table, including [insert_tag] Arg 1: WML table to interpolate into ...
Definition: lua_wml.cpp:141
#define LUA_TUSERDATA
Definition: lua.h:71
LUA_API void lua_pushboolean(lua_State *L, int b)
Definition: lapi.cpp:557
const attribute_value * get(config_key_type key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
Definition: config.cpp:742
static int intf_wml_merge(lua_State *L)
Merges two WML tables Arg 1: base table Arg 2: table to merge in.
Definition: lua_wml.cpp:168
#define lua_tonumber(L, i)
Definition: lua.h:341
static int intf_parse_wml(lua_State *L)
Parses a WML string into a config; does not preprocess or validate.
Definition: lua_wml.cpp:101
void append_children(const config &cfg)
Adds children from cfg.
Definition: config.cpp:241
#define lua_pop(L, n)
Definition: lua.h:344
config get_diff(const config &c) const
A function to get the differences between this object, and &#39;c&#39;, as another config object...
Definition: config.cpp:947
void merge_with(const config &c)
Merge config &#39;c&#39; into this config, overwriting this config&#39;s values.
Definition: config.cpp:1167
One of the realizations of serialization/validator.hpp abstract validator.
static int intf_wml_matches_filter(lua_State *L)
Tests if a WML table matches a filter Arg 1: table to test Arg 2: filter.
Definition: lua_wml.cpp:155
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
Definition: parser.cpp:762
LUALIB_API int luaL_argerror(lua_State *L, int arg, const char *extramsg)
Definition: lauxlib.cpp:164
LUA_API int lua_isstring(lua_State *L, int idx)
Definition: lapi.cpp:283
bool luaW_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:890
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:625
bool validate_wml() const
Returns true if this object represents valid WML, i.e.
Definition: config.cpp:1391
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:39
static int intf_load_wml(lua_State *L)
Loads a WML file into a config.
Definition: lua_wml.cpp:52
config get_parsed_config() const
Definition: variable.cpp:176
void apply_diff(const config &diff, bool track=false)
A function to apply a diff config onto this config object.
Definition: config.cpp:1068
#define lua_newtable(L)
Definition: lua.h:346
int luaW_open(lua_State *L)
Definition: lua_wml.cpp:244
static int intf_clone_wml(lua_State *L)
Returns a clone (deep copy) of the passed config, which can be either a normal config or a vconfig If...
Definition: lua_wml.cpp:122
LUA_API void lua_len(lua_State *L, int idx)
Definition: lapi.cpp:1156
lu_byte right
Definition: lparser.cpp:1027
bool luaW_toconfig(lua_State *L, int index, config &cfg)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:742
std::size_t i
Definition: function.cpp:933
std::string get_wml_location(const std::string &filename, const std::string &current_dir)
Returns a complete path to the actual WML file or directory or an empty string if the file isn&#39;t pres...
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:819
#define lua_tostring(L, i)
Definition: lua.h:366
static int intf_wml_patch(lua_State *L)
Applies a diff to a WML table Arg 1: base table Arg 2: WML diff.
Definition: lua_wml.cpp:208
void merge_attributes(const config &)
Definition: config.cpp:788
lu_byte left
Definition: lparser.cpp:1026
void luaW_pushvconfig(lua_State *L, const vconfig &cfg)
Pushes a vconfig on the top of the stack.
Definition: lua_common.cpp:527
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
const config & get_config() const
Definition: variable.hpp:82
static int intf_wml_diff(lua_State *L)
Computes a diff of two WML tables Arg 1: left table Arg 2: right table.
Definition: lua_wml.cpp:195
LUA_API int lua_geti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:628
std::map< std::string, struct preproc_define > preproc_map
static int intf_wml_valid(lua_State *L)
Tests if a table represents a valid WML table Arg 1: table.
Definition: lua_wml.cpp:234
Realization of serialization/validator.hpp abstract validator.
LUALIB_API void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup)
Definition: lauxlib.cpp:934
static int intf_wml_tostring(lua_State *L)
Dumps a wml table or userdata wml object into a pretty string.
Definition: lua_wml.cpp:37
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
mock_char c
#define LUA_TTABLE
Definition: lua.h:69
static map_location::DIRECTION n
#define luaL_optstring(L, n, d)
Definition: lauxlib.h:125
LUA_API const char * lua_pushstring(lua_State *L, const char *s)
Definition: lapi.cpp:491
#define LUA_TBOOLEAN
Definition: lua.h:65
#define luaL_checkstring(L, n)
Definition: lauxlib.h:124