34 #define ERR_NG LOG_STREAM(err, log_engine)
45 ERR_NG <<
"Formula in WML string cannot be evaluated due to "
46 <<
e.type <<
"\n\t--> \"";
61 using expr_table = std::map<std::string, expression_ptr>;
69 std::ostringstream
expr;
83 std::string
str()
const
99 const char*
const formula::id_chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
105 , symbols_(symbols ? symbols : managed_symbols_.
get())
107 std::vector<tk::token> tokens;
108 std::string::const_iterator i1 = text.begin(), i2 = text.end();
111 bool wfl_keyword =
false;
114 std::vector<std::pair<std::string, int>> files;
117 std::set<std::string> filenames;
119 files.emplace_back(
"formula", 1);
120 filenames.insert(
"formula");
130 if(current_type == tk::token_type::whitespace) {
132 }
else if(current_type == tk::token_type::comment) {
136 std::string comment = std::string(tokens.back().begin, tokens.back().end);
137 for(
const auto& str_it : comment) {
143 files.back().second += counter;
145 }
else if(current_type == tk::token_type::eol) {
146 files.back().second++;
148 }
else if((current_type == tk::token_type::keyword) && (std::string(tokens.back().begin, tokens.back().end) ==
"wfl")) {
151 }
else if((current_type == tk::token_type::keyword) && (std::string(tokens.back().begin, tokens.back().end) ==
"wflend")) {
152 if(files.size() > 1) {
154 filenames_it = filenames.find(files.back().first);
160 }
else if(wfl_keyword) {
161 if(current_type == tk::token_type::string_literal) {
162 std::string
str = std::string(tokens.back().begin, tokens.back().end);
163 files.emplace_back(
str , 1);
165 auto [pos, success] = filenames.insert(
str);
176 throw formula_error(
"Expected string after the 'wfl'",
"wfl",
"", 0);
180 tokens.back().filename = &(*filenames_it);
181 tokens.back().line_number = files.back().second;
186 std::string
str =
"";
187 if(!tokens.empty()) {
188 tk::token* tok_it = &tokens[0] + tokens.size()-1;
189 while(( tok_it != &tokens[0] ) && (tok_it->
line_number == tokens.back().line_number)) {
193 if(tok_it != &tokens[0] && tok_it != &tokens[0] + tokens.size() -1) {
200 throw formula_error(
e.description_,
str +
e.formula_, *filenames_it, files.back().second);
204 if(files.size() > 1) {
205 throw formula_error(
"Missing 'wflend', make sure each .wfl file ends with it",
"",
"", 0);
208 if(!tokens.empty()) {
211 expr_ = std::make_shared<null_expression>();
219 , symbols_(symbols ? symbols : managed_symbols_.
get())
224 expr_ = std::make_shared<null_expression>();
243 return expr_->evaluate(variables, fdb);
245 PLAIN_LOG <<
"formula type error: " <<
e.message;
253 return execute(null_callable,fdb);
258 const std::string& file,
int line)
265 std::stringstream ss;
268 <<
"\nError: " <<
type;
283 virtual std::string
str()
const
285 return "{function_list_expression()}";
291 std::vector<variant> res;
293 res.emplace_back(fcn_name);
312 std::vector<variant> res;
313 res.reserve(
items_.size());
315 res.push_back(
i->evaluate(variables,
add_debug_info(fdb, 0,
"[list element]")));
327 bool first_item =
true;
348 virtual std::string
str()
const
352 for(std::vector<expression_ptr>::const_iterator
i =
items_.begin(); (
i !=
items_.end()) && (
i + 1 !=
items_.end()) ;
i += 2) {
358 s << (*(
i+1))->str();
370 std::map<variant,variant> res;
371 for(std::vector<expression_ptr>::const_iterator
i =
items_.begin(); (
i !=
items_.end()) && (
i + 1 !=
items_.end()) ;
i += 2) {
392 }
else if(op ==
"-") {
395 throw formula_error(
"Illegal unary operator: '" + op +
"'" ,
"",
"", 0);
399 virtual std::string
str()
const
443 }
else if(key ==
"empty") {
445 }
else if(key ==
"char" || key ==
"chars") {
446 std::vector<variant> chars;
448 chars.emplace_back(std::string(1,
c));
452 }
else if(key ==
"word" || key ==
"words") {
453 std::vector<variant> words;
455 std::size_t next_space = 0;
457 std::size_t last_space = next_space;
458 next_space = str.find_first_of(
" \t", next_space);
459 words.emplace_back(str.substr(last_space, next_space - last_space));
460 next_space = str.find_first_not_of(
" \t", next_space);
461 }
while(next_space != std::string::npos);
464 }
else if(key ==
"item" || key ==
"items") {
466 std::vector<variant> items;
467 items.reserve(
split.size());
468 for(
const std::string&
s :
split) {
469 items.emplace_back(
s);
499 }
else if(key ==
"empty") {
501 }
else if(key ==
"first") {
507 }
else if(key ==
"last") {
532 for(
const auto& v :
map_) {
540 std::string key = key_variant.
as_string();
543 if(!isalpha(
c) &&
c !=
'_') {
557 const variant key_variant(key);
559 return map_[key_variant];
560 }
else if(key ==
"size") {
562 }
else if(key ==
"empty") {
621 return right_->evaluate(callable,fdb);
627 return right_->evaluate(callable,fdb);
633 return right_->evaluate(callable,fdb);
650 :
left_(std::move(left)),
key_(std::move(key))
656 s <<
left_->str() <<
'[' <<
key_->str() <<
']';
683 }
else if(op ==
"<=") {
685 }
else if(op ==
"!=") {
687 }
else if(op ==
"and") {
689 }
else if(op ==
"or") {
691 }
else if(op ==
".+") {
693 }
else if(op ==
".-") {
695 }
else if(op ==
".*") {
697 }
else if(op ==
"./") {
699 }
else if(op ==
"..") {
701 }
else if(op ==
"in") {
721 return left.
as_bool() ==
false ? left : right;
723 return left.
as_bool() ? left : right;
743 return variant(right.contains(left));
765 PLAIN_LOG <<
"ERROR: Unimplemented operator!";
773 while(faces > 0 && num_rolls-- > 0) {
781 enum OP {
AND,
OR,
NEQ,
LTE,
GTE,
OP_CAT,
OP_IN,
GT=
'>',
LT=
'<',
EQ=
'=',
RAN=
'~',
782 ADD=
'+',
SUB=
'-',
MUL=
'*',
DIV=
'/',
ADDL,
SUBL,
MULL,
DIVL,
DICE=
'd',
POW=
'^',
MOD=
'%' };
795 ,
table_(std::move(table))
809 for(expr_table::const_iterator
i =
table_->begin();
i !=
table_->end(); ++
i) {
844 for(
const expr_table::value_type &a : *
clauses_) {
845 s <<
", [" << a.first <<
"] -> ["<< a.second->str()<<
"]";
936 int bracket_depth = 0;
938 while(j !=
str.end() && (bracket_depth > 0 || *j !=
']')) {
941 }
else if(*j ==
']' && bracket_depth > 0) {
951 const std::string formula_str(
i+1, j);
952 const int pos = std::distance(
str.begin(),
i);
953 if(j -
i == 2 && (
i[1] ==
'(' ||
i[1] ==
'\'' ||
i[1] ==
')')) {
959 }
else if(*
i ==
')') {
963 i =
str.erase(
i + 1);
966 i =
str.erase(
i, j+1);
974 e.filename +=
" - string substitution";
978 subs_.push_back(sub);
989 int j = res.size() - 1;
991 for(
const auto& sub :
subs_) {
992 for(; j >= sub.pos && j >= 0; j--) {
994 res.replace(j, 1,
"[']");
995 }
else if(res[j] ==
'[') {
996 res.replace(j, 1,
"[(]");
997 }
else if(res[j] ==
']') {
998 res.replace(j, 1,
"[)]");
1002 const std::string
str =
"[" + sub.calculation->str() +
"]";
1003 res.insert(sub.pos,
str);
1006 for(; j >= 0; j--) {
1007 if(res[j] ==
'\'') {
1008 res.replace(j, 1,
"[']");
1009 }
else if(res[j] ==
'[') {
1010 res.replace(j, 1,
"[(]");
1011 }
else if(res[j] ==
']') {
1012 res.replace(j, 1,
"[)]");
1016 return "'" + res +
"'";
1027 for(std::size_t
i = 0;
i <
subs_.size(); ++
i) {
1028 const int j =
subs_.size() -
i - 1;
1031 const std::string
str = sub.
calculation->evaluate(variables,fdb).string_cast();
1032 res.insert(sub.
pos,
str);
1056 static std::map<std::string,int> precedence_map;
1057 if(precedence_map.empty()) {
1059 precedence_map[
"not"] = ++
n;
1060 precedence_map[
"where"] = ++
n;
1061 precedence_map[
"or"] = ++
n;
1062 precedence_map[
"and"] = ++
n;
1063 precedence_map[
"="] = ++
n;
1064 precedence_map[
"!="] =
n;
1065 precedence_map[
"<"] =
n;
1066 precedence_map[
">"] =
n;
1067 precedence_map[
"<="] =
n;
1068 precedence_map[
">="] =
n;
1069 precedence_map[
"in"] =
n;
1070 precedence_map[
"~"] = ++
n;
1071 precedence_map[
"+"] = ++
n;
1072 precedence_map[
"-"] =
n;
1073 precedence_map[
".."] =
n;
1074 precedence_map[
"*"] = ++
n;
1075 precedence_map[
"/"] =
n;
1076 precedence_map[
"%"] = ++
n;
1077 precedence_map[
"^"] = ++
n;
1078 precedence_map[
"d"] = ++
n;
1079 precedence_map[
"."] = ++
n;
1082 assert(precedence_map.count(std::string(
t.begin,
t.end)));
1083 return precedence_map[std::string(
t.begin,
t.end)];
1090 if(i1->
type == tk::token_type::lparens) {
1096 while((i1->
type != tk::token_type::rparens) && (i1 != i2)) {
1097 if(i1->
type == tk::token_type::identifier) {
1098 if(std::string((i1+1)->begin, (i1+1)->end) ==
"*") {
1099 res->push_back(std::string(i1->
begin, i1->
end) + std::string(
"*"));
1102 res->push_back(std::string(i1->
begin, i1->
end));
1104 }
else if(i1->
type == tk::token_type::comma) {
1113 if(i1->
type != tk::token_type::rparens) {
1126 if(i1->
type == tk::token_type::lparens || i1->
type == tk::token_type::lsquare ) {
1128 }
else if(i1->
type == tk::token_type::rparens || i1->
type == tk::token_type::rsquare ) {
1130 }
else if(i1->
type == tk::token_type::comma && !parens) {
1147 bool check_pointer =
false;
1151 if(i1->
type == tk::token_type::lparens || i1->
type == tk::token_type::lsquare) {
1153 }
else if(i1->
type == tk::token_type::rparens || i1->
type == tk::token_type::rsquare) {
1155 }
else if(i1->
type == tk::token_type::pointer && !parens ) {
1156 if(!check_pointer) {
1157 check_pointer =
true;
1163 }
else if(i1->
type == tk::token_type::comma && !parens ) {
1165 check_pointer =
false;
1184 const tk::token* original_i1_cached = i1;
1187 std::string var_name;
1190 if(i1->
type == tk::token_type::lparens || i1->
type == tk::token_type::lsquare) {
1192 }
else if(i1->
type == tk::token_type::rparens || i1->
type == tk::token_type::rsquare) {
1194 }
else if(!parens) {
1195 if(i1->
type == tk::token_type::comma) {
1196 if(var_name.empty()) {
1197 throw formula_error(
"There is 'where <expression>' but 'where name=<expression>' was needed",
1204 }
else if(i1->
type == tk::token_type::operator_token) {
1205 std::string op_name(i1->
begin, i1->
end);
1207 if(op_name ==
"=") {
1208 if(beg->
type != tk::token_type::identifier) {
1209 if(i1 == original_i1_cached) {
1210 throw formula_error(
"There is 'where <expression>' but 'where name=<expression>' was needed",
1213 throw formula_error(
"There is 'where <expression>=<expression>' but 'where name=<expression>' was needed",
1216 }
else if(beg+1 != i1) {
1217 throw formula_error(
"There is 'where name <expression>=<expression>' but 'where name=<expression>' was needed",
1219 }
else if(!var_name.empty()) {
1220 throw formula_error(
"There is 'where name=name=<expression>' but 'where name=<expression>' was needed",
1224 var_name.insert(var_name.end(), beg->
begin, beg->
end);
1233 if(var_name.empty()) {
1234 throw formula_error(
"There is 'where <expression>' but 'where name=<expression>' was needed",
1248 std::unique_ptr<function_symbol_table> temp_functions;
1251 symbols = temp_functions.get();
1256 if(i1->
type == tk::token_type::keyword && (i1 + 1)->type == tk::token_type::identifier) {
1257 if(std::string(i1->
begin, i1->
end) ==
"def") {
1259 const std::string formula_name = std::string(i1->
begin, i1->
end);
1261 std::vector<std::string> args;
1265 while((i1 != i2) && (i1->
type != tk::token_type::semicolon)) {
1269 const std::string precond =
"";
1270 if(symbols ==
nullptr) {
1275 std::make_shared<user_formula_function>(
1276 formula_name, std::make_shared<const formula>(beg, i1, symbols),
1281 if((i1 == i2) || (i1 == (i2-1))) {
1282 return std::make_shared<function_list_expression>(symbols);
1291 bool operator_group =
false;
1294 if(
i->type == tk::token_type::lparens ||
i->type == tk::token_type::lsquare) {
1296 }
else if(
i->type == tk::token_type::rparens ||
i->type == tk::token_type::rsquare) {
1298 }
else if(parens == 0 &&
i->type == tk::token_type::operator_token) {
1301 if(*
i->begin !=
'^' || op ==
nullptr || *op->
begin !=
'^') {
1305 operator_group =
true;
1307 operator_group =
false;
1314 if(i1->
type == tk::token_type::lparens && (i2 - 1)->type == tk::token_type::rparens) {
1315 if(i1 + 1 == i2 - 1) {
1319 }
else if((i2 - 1)->
type == tk::token_type::rsquare) {
1321 if(i2 - i1 == 3 && i1->
type == tk::token_type::lsquare && (i1+1)->type == tk::token_type::pointer) {
1322 return std::make_shared<map_expression>(std::vector<expression_ptr>());
1326 int square_parens = 0;
1328 while ((tok->
type != tk::token_type::lsquare || square_parens) && tok != i1) {
1329 if(tok->
type == tk::token_type::rsquare) {
1331 }
else if(tok->
type == tk::token_type::lsquare) {
1333 }
else if((tok->
type == tk::token_type::pointer) && !square_parens ) {
1339 if(tok->
type == tk::token_type::lsquare) {
1342 std::vector<expression_ptr> args;
1346 return std::make_shared<map_expression>(args);
1349 return std::make_shared<list_expression>(args);
1354 return std::make_shared<square_bracket_expression>(
1363 }
else if(i2 - i1 == 1) {
1364 if(i1->
type == tk::token_type::keyword) {
1365 if(std::string(i1->
begin, i1->
end) ==
"functions") {
1366 return std::make_shared<function_list_expression>(symbols);
1368 }
else if(i1->
type == tk::token_type::identifier) {
1369 return std::make_shared<identifier_expression>(std::string(i1->
begin, i1->
end));
1370 }
else if(i1->
type == tk::token_type::integer) {
1372 return std::make_shared<integer_expression>(
n);
1373 }
else if(i1->
type == tk::token_type::decimal) {
1375 while(*dot !=
'.') {
1383 if(literal_end - dot > 4) {
1384 literal_end = dot + 4;
1391 int multiplicator = 100;
1392 while(dot != literal_end) {
1393 f += (*dot - 48) * multiplicator;
1394 multiplicator /= 10;
1398 return std::make_shared<decimal_expression>(
n,
f);
1399 }
else if(i1->
type == tk::token_type::string_literal) {
1400 return std::make_shared<string_expression>(std::string(i1->
begin + 1, i1->
end - 1));
1402 }
else if(i1->
type == tk::token_type::identifier &&
1403 (i1+1)->type == tk::token_type::lparens &&
1404 (i2-1)->type == tk::token_type::rparens)
1406 const tk::token* function_call_begin = i1, *function_call_end = i2;
1407 int nleft = 0, nright = 0;
1409 if(
i->type == tk::token_type::lparens) {
1411 }
else if(
i->type == tk::token_type::rparens) {
1416 if(nleft == nright) {
1417 std::vector<expression_ptr> args;
1444 const std::string op_name(op->
begin,op->
end);
1446 if(op_name ==
".") {
1455 if(op_name ==
"where") {
1459 return std::make_shared<where_expression>(
parse_expression(i1, op, symbols), table);
int get_random_int(int min, int max)
decimal_expression(int i, int f)
variant execute(const formula_callable &, formula_debugger *) const
void get_inputs(formula_input_vector &inputs) const
variant get_value(const std::string &key) const
const formula_callable & local_
dot_callable(const formula_callable &global, const formula_callable &local)
const formula_callable & global_
variant execute(const formula_callable &variables, formula_debugger *fdb) const
dot_expression(expression_ptr left, expression_ptr right)
Classes that encapsulate and handle the various formula functions.
function_list_expression(function_symbol_table *symbols)
variant execute(const formula_callable &, formula_debugger *) const
virtual std::string str() const
function_symbol_table * symbols_
static std::shared_ptr< function_symbol_table > get_builtins()
expression_ptr create_function(const std::string &fn, const std::vector< expression_ptr > &args) const
void add_function(const std::string &name, formula_function_ptr &&fcn)
std::set< std::string > get_function_names() const
identifier_expression(const std::string &id)
variant execute(const formula_callable &variables, formula_debugger *) const
variant execute(const formula_callable &, formula_debugger *) const
integer_expression(int i)
void get_inputs(formula_input_vector &inputs) const
list_callable(const variant &list)
variant get_value(const std::string &key) const
list_expression(const std::vector< expression_ptr > &items)
variant execute(const formula_callable &variables, formula_debugger *fdb) const
std::vector< expression_ptr > items_
map_callable(const variant &map)
variant get_value(const std::string &key) const
void get_inputs(formula_input_vector &inputs) const
std::vector< expression_ptr > items_
map_expression(const std::vector< expression_ptr > &items)
virtual std::string str() const
variant execute(const formula_callable &variables, formula_debugger *fdb) const
variant execute(const formula_callable &, formula_debugger *) const
variant execute(const formula_callable &variables, formula_debugger *fdb) const
static int dice_roll(int num_rolls, int faces)
operator_expression(const std::string &op, expression_ptr left, expression_ptr right)
square_bracket_expression(expression_ptr left, expression_ptr key)
variant execute(const formula_callable &variables, formula_debugger *fdb) const
string_callable(const variant &string)
void get_inputs(formula_input_vector &inputs) const
variant get_value(const std::string &key) const
string_expression(std::string str)
variant execute(const formula_callable &variables, formula_debugger *fdb) const
std::vector< substitution > subs_
unary_operator_expression(const std::string &op, expression_ptr arg)
virtual std::string str() const
variant execute(const formula_callable &variables, formula_debugger *fdb) const
variant concatenate(const variant &v) const
int as_int(int fallback=0) const
Returns the variant's value as an integer.
const_formula_callable_ptr as_callable() const
std::size_t num_elements() const
variant get_member(const std::string &name) const
variant list_elements_mul(const variant &v) const
variant list_elements_add(const variant &v) const
const std::string & as_string() const
std::string string_cast() const
variant list_elements_div(const variant &v) const
bool as_bool() const
Returns a boolean state of the variant value.
variant build_range(const variant &v) const
const std::map< variant, variant > & as_map() const
variant list_elements_sub(const variant &v) const
variant execute(const formula_callable &variables, formula_debugger *fdb) const
where_expression(expression_ptr body, expr_table_ptr clauses)
const formula_callable & base_
variant get_value(const std::string &key) const
where_variables(const formula_callable &base, expr_table_ptr table, formula_debugger *fdb)
void get_inputs(formula_input_vector &inputs) const
expr_table_evaluated evaluated_table_
formula_debugger * debugger_
std::string id
Text to match against addon_info.tags()
Standard logging facilities (interface).
void line(int from_x, int from_y, int to_x, int to_y)
Draw a line.
bool is_map(const std::string &filename)
Returns true if the file ends with the mapfile extension.
rng * generator
This generator is automatically synced during synced context.
struct utils::detail::formula_initer init
std::string(* evaluate_formula)(const std::string &formula)
std::string evaluate_formula_impl(const std::string &)
int stoi(std::string_view str)
Same interface as std::stoi and meant as a drop in replacement, except:
std::vector< std::string > parenthetical_split(std::string_view val, const char separator, std::string_view left, std::string_view right, const int flags)
Splits a string based either on a separator, except then the text appears within specified parenthesi...
std::vector< std::string > split(const config_attribute_value &val)
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
token get_token(iterator &i1, const iterator i2)
std::string::const_iterator iterator
formula_debugger * add_debug_info(formula_debugger *fdb, int arg_number, const std::string &f_name)
std::map< std::string, expression_ptr > expr_table
static void parse_function_args(const tk::token *&i1, const tk::token *i2, std::vector< std::string > *res)
std::vector< formula_input > formula_input_vector
std::shared_ptr< const formula > const_formula_ptr
std::shared_ptr< formula_expression > expression_ptr
static int operator_precedence(const tk::token &t)
Functions to handle the actual parsing of WFL.
std::shared_ptr< expr_table > expr_table_ptr
std::shared_ptr< formula > formula_ptr
static void parse_set_args(const tk::token *i1, const tk::token *i2, std::vector< expression_ptr > *res, function_symbol_table *symbols)
std::map< std::string, variant > expr_table_evaluated
expression_ptr parse_expression(const tk::token *i1, const tk::token *i2, function_symbol_table *symbols)
static std::string tokens_to_string(const tk::token *i1, const tk::token *i2)
static void parse_where_clauses(const tk::token *i1, const tk::token *i2, const expr_table_ptr &res, function_symbol_table *symbols)
static void parse_args(const tk::token *i1, const tk::token *i2, std::vector< expression_ptr > *res, function_symbol_table *symbols)
std::string filename
Filename.
const_formula_ptr calculation
const std::string * filename
static map_location::direction n
static map_location::direction s