1 
2 /*
3    Copyright (C) 2008 - 2018 by David White <dave@whitevine.net>
4    Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY.
12 
13    See the COPYING file for more details.
14 */
15 
16 #pragma once
17 
18 #include "formula/callable.hpp"
19 #include "formula/formula.hpp"
20 
21 #include <set>
22 #include <string>
23 
24 namespace wfl
25 {
26 /** Helper macro to declare an associated class for a WFL function. */
27 #define DEFINE_WFL_FUNCTION(name, min_args, max_args)                                                                  \
28 	class name##_function : public function_expression                                                                 \
29 	{                                                                                                                  \
30 	public:                                                                                                            \
31 		explicit name##_function(const args_list& args)                                                                \
32 			: function_expression(#name, args, min_args, max_args)                                                     \
33 		{                                                                                                              \
34 		}                                                                                                              \
35                                                                                                                        \
36 	private:                                                                                                           \
37 		variant execute(const formula_callable& variables, formula_debugger* fdb) const;                               \
38 	};                                                                                                                 \
39                                                                                                                        \
40 	variant name##_function::execute(const formula_callable& variables, formula_debugger* fdb) const
41 
42 
43 /**
44  * Declares a function `name` in the local function table `functions_table`.
45  * The function must be defined by a `name_function` class which is accessible in the current scope.
46  */
47 #define DECLARE_WFL_FUNCTION(name)                                                                                     \
48 	functions_table.add_function(#name, std::make_shared<builtin_formula_function<name##_function>>(#name))
49 
50 /**
51  * Provides debugging information for error messages.
52  */
53 struct call_stack_manager
54 {
55 	explicit call_stack_manager(const std::string& str);
56 	call_stack_manager(const call_stack_manager&) = delete;
57 	call_stack_manager& operator=(const call_stack_manager&) = delete;
58 	~call_stack_manager();
59 
60 	static std::string get();
61 };
62 
63 class formula_expression
64 {
65 public:
formula_expression(const std::string & name="")66 	explicit formula_expression(const std::string& name = "")
67 		: name_(name)
68 	{
69 	}
70 
~formula_expression()71 	virtual ~formula_expression()
72 	{
73 	}
74 
evaluate(const formula_callable & variables,formula_debugger * fdb=nullptr) const75 	variant evaluate(const formula_callable& variables, formula_debugger* fdb = nullptr) const
76 	{
77 		call_stack_manager manager(name_);
78 
79 		if(fdb != nullptr) {
80 			return evaluate_arg_callback(*fdb, *this, variables);
81 		} else {
82 			return execute(variables, fdb);
83 		}
84 	}
85 
get_name() const86 	std::string get_name() const
87 	{
88 		return name_;
89 	}
90 
91 	virtual std::string str() const = 0;
92 
93 private:
94 	virtual variant execute(const formula_callable& variables, formula_debugger* fdb = nullptr) const = 0;
95 
96 	const std::string name_;
97 	friend class formula_debugger;
98 };
99 
100 typedef std::shared_ptr<formula_expression> expression_ptr;
101 
102 class function_expression : public formula_expression
103 {
104 public:
105 	typedef std::vector<expression_ptr> args_list;
106 
function_expression(const std::string & name,const args_list & args,int min_args=-1,int max_args=-1)107 	explicit function_expression(const std::string& name, const args_list& args, int min_args = -1, int max_args = -1)
108 		: formula_expression(name)
109 		, args_(args)
110 	{
111 		if(min_args >= 0 && args_.size() < static_cast<size_t>(min_args)) {
112 			throw formula_error("Too few arguments", "", "", 0);
113 		}
114 
115 		if(max_args >= 0 && args_.size() > static_cast<size_t>(max_args)) {
116 			throw formula_error("Too many arguments", "", "", 0);
117 		}
118 	}
119 
120 	virtual std::string str() const;
121 
122 protected:
args() const123 	const args_list& args() const
124 	{
125 		return args_;
126 	}
127 
128 private:
129 	args_list args_;
130 };
131 
132 class key_value_pair : public formula_callable
133 {
134 public:
key_value_pair(const variant & key,const variant & value)135 	explicit key_value_pair(const variant& key, const variant& value)
136 		: key_(key)
137 		, value_(value)
138 	{
139 	}
140 
141 	void serialize_to_string(std::string& str) const override;
142 
143 private:
144 	variant key_;
145 	variant value_;
146 
147 	variant get_value(const std::string& key) const override;
148 
149 	void get_inputs(formula_input_vector& inputs) const override;
150 };
151 
152 class formula_function_expression : public function_expression
153 {
154 public:
155 	explicit formula_function_expression(const std::string& name,
156 			const args_list& args,
157 			const_formula_ptr formula,
158 			const_formula_ptr precondition,
159 			const std::vector<std::string>& arg_names);
160 
161 private:
162 	variant execute(const formula_callable& variables, formula_debugger* fdb) const;
163 
164 	const_formula_ptr formula_;
165 	const_formula_ptr precondition_;
166 
167 	std::vector<std::string> arg_names_;
168 
169 	int star_arg_;
170 };
171 
172 typedef std::shared_ptr<function_expression> function_expression_ptr;
173 
174 class formula_function
175 {
176 public:
formula_function(const std::string name)177 	formula_function(const std::string name)
178 		: name_(name)
179 	{
180 	}
181 
182 	virtual function_expression_ptr generate_function_expression(const std::vector<expression_ptr>& args) const = 0;
183 
~formula_function()184 	virtual ~formula_function()
185 	{
186 	}
187 
188 protected:
189 	std::string name_;
190 };
191 
192 class user_formula_function : public formula_function
193 {
194 public:
user_formula_function(const std::string & name,const_formula_ptr formula,const_formula_ptr precondition,const std::vector<std::string> & args)195 	user_formula_function(const std::string& name,
196 			const_formula_ptr formula,
197 			const_formula_ptr precondition,
198 			const std::vector<std::string>& args)
199 		: formula_function(name)
200 		, formula_(formula)
201 		, precondition_(precondition)
202 		, args_(args)
203 	{
204 	}
205 
206 	function_expression_ptr generate_function_expression(const std::vector<expression_ptr>& args) const;
207 
208 private:
209 	const_formula_ptr formula_;
210 	const_formula_ptr precondition_;
211 	std::vector<std::string> args_;
212 };
213 
214 template<typename T>
215 class builtin_formula_function : public formula_function
216 {
217 public:
builtin_formula_function(const std::string & name)218 	builtin_formula_function(const std::string& name)
219 		: formula_function(name)
220 	{
221 	}
222 
generate_function_expression(const std::vector<expression_ptr> & args) const223 	function_expression_ptr generate_function_expression(const std::vector<expression_ptr>& args) const
224 	{
225 		return std::make_shared<T>(args);
226 	}
227 };
228 
229 typedef std::shared_ptr<formula_function> formula_function_ptr;
230 typedef std::map<std::string, formula_function_ptr> functions_map;
231 
232 class function_symbol_table
233 {
234 public:
235 	explicit function_symbol_table(std::shared_ptr<function_symbol_table> parent = nullptr);
236 
237 	void add_function(const std::string& name, formula_function_ptr&& fcn);
238 
239 	expression_ptr create_function(const std::string& fn, const std::vector<expression_ptr>& args) const;
240 
241 	std::set<std::string> get_function_names() const;
242 
empty() const243 	bool empty() const
244 	{
245 		return custom_formulas_.empty() && (parent == nullptr || parent->empty());
246 	}
247 
248 	static std::shared_ptr<function_symbol_table> get_builtins();
249 
250 private:
251 	std::shared_ptr<function_symbol_table> parent;
252 	functions_map custom_formulas_;
253 
254 	enum builtins_tag_t { builtins_tag };
function_symbol_table(builtins_tag_t)255 	function_symbol_table(builtins_tag_t)
256 	{
257 	}
258 };
259 
260 class action_function_symbol_table : public function_symbol_table
261 {
262 public:
263 	action_function_symbol_table(std::shared_ptr<function_symbol_table> parent = nullptr);
264 };
265 
266 class wrapper_formula : public formula_expression
267 {
268 public:
wrapper_formula()269 	wrapper_formula()
270 		: arg_()
271 	{
272 	}
273 
wrapper_formula(expression_ptr arg)274 	wrapper_formula(expression_ptr arg)
275 		: formula_expression(arg ? arg->get_name() : "")
276 		, arg_(arg)
277 	{
278 	}
279 
~wrapper_formula()280 	virtual ~wrapper_formula()
281 	{
282 	}
283 
str() const284 	virtual std::string str() const
285 	{
286 		if(arg_) {
287 			return arg_->str();
288 		} else {
289 			return "";
290 		}
291 	}
292 
293 private:
execute(const formula_callable & variables,formula_debugger * fdb=nullptr) const294 	virtual variant execute(const formula_callable& variables, formula_debugger* fdb = nullptr) const
295 	{
296 		if(arg_) {
297 			return arg_->evaluate(variables, fdb);
298 		} else {
299 			return variant();
300 		}
301 	}
302 
303 	expression_ptr arg_;
304 };
305 } // end namespace wfl
306