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 fai_keyword =
false;
114 bool wfl_keyword =
false;
117 std::vector<std::pair<std::string, int>> files;
120 std::set<std::string> filenames;
122 files.emplace_back(
"formula", 1);
123 filenames.insert(
"formula");
133 if(current_type == tk::token_type::whitespace) {
135 }
else if(current_type == tk::token_type::comment) {
139 std::string comment = std::string(tokens.back().begin, tokens.back().end);
140 for(
const auto& str_it : comment) {
146 files.back().second += counter;
148 }
else if(current_type == tk::token_type::eol) {
149 files.back().second++;
151 }
else if((current_type == tk::token_type::keyword) && (std::string(tokens.back().begin, tokens.back().end) ==
"fai")) {
154 }
else if((current_type == tk::token_type::keyword) && (std::string(tokens.back().begin, tokens.back().end) ==
"wfl")) {
157 }
else if((current_type == tk::token_type::keyword) && (std::string(tokens.back().begin, tokens.back().end) ==
"faiend")) {
158 if(files.size() > 1) {
160 filenames_it = filenames.find(files.back().first);
166 }
else if((current_type == tk::token_type::keyword) && (std::string(tokens.back().begin, tokens.back().end) ==
"wflend")) {
167 if(files.size() > 1) {
169 filenames_it = filenames.find(files.back().first);
175 }
else if(fai_keyword || wfl_keyword) {
176 if(current_type == tk::token_type::string_literal) {
177 std::string
str = std::string(tokens.back().begin, tokens.back().end);
178 files.emplace_back(
str , 1);
180 auto [pos, success] = filenames.insert(
str);
197 throw formula_error(
"Expected string after the 'fai'",
"fai",
"", 0);
199 throw formula_error(
"Expected string after the 'wfl'",
"wfl",
"", 0);
204 tokens.back().filename = &(*filenames_it);
205 tokens.back().line_number = files.back().second;
210 std::string
str =
"";
211 if(!tokens.empty()) {
212 tk::token* tok_it = &tokens[0] + tokens.size()-1;
213 while(( tok_it != &tokens[0] ) && (tok_it->
line_number == tokens.back().line_number)) {
217 if(tok_it != &tokens[0] && tok_it != &tokens[0] + tokens.size() -1) {
224 throw formula_error(
e.description_,
str +
e.formula_, *filenames_it, files.back().second);
228 if(files.size() > 1) {
229 throw formula_error(
"Missing 'wflend', make sure each .wfl file ends with it",
"",
"", 0);
232 if(!tokens.empty()) {
235 expr_ = std::make_shared<null_expression>();
243 , symbols_(symbols ? symbols : managed_symbols_.
get())
248 expr_ = std::make_shared<null_expression>();
264 return expr_->evaluate(variables, fdb);
266 PLAIN_LOG <<
"formula type error: " <<
e.message;
274 return execute(null_callable,fdb);
279 const std::string& file,
int line)
286 std::stringstream ss;
289 <<
"\nError: " <<
type;
304 virtual std::string
str()
const
306 return "{function_list_expression()}";
312 std::vector<variant> res;
314 res.emplace_back(fcn_name);
333 std::vector<variant> res;
334 res.reserve(
items_.size());
336 res.push_back(
i->evaluate(variables,
add_debug_info(fdb, 0,
"[list element]")));
348 bool first_item =
true;
369 virtual std::string
str()
const
373 for(std::vector<expression_ptr>::const_iterator
i =
items_.begin(); (
i !=
items_.end()) && (
i + 1 !=
items_.end()) ;
i += 2) {
379 s << (*(
i+1))->str();
391 std::map<variant,variant> res;
392 for(std::vector<expression_ptr>::const_iterator
i =
items_.begin(); (
i !=
items_.end()) && (
i + 1 !=
items_.end()) ;
i += 2) {
413 }
else if(op ==
"-") {
416 throw formula_error(
"Illegal unary operator: '" + op +
"'" ,
"",
"", 0);
420 virtual std::string
str()
const
464 }
else if(key ==
"empty") {
466 }
else if(key ==
"char" || key ==
"chars") {
467 std::vector<variant> chars;
469 chars.emplace_back(std::string(1,
c));
473 }
else if(key ==
"word" || key ==
"words") {
474 std::vector<variant> words;
476 std::size_t next_space = 0;
478 std::size_t last_space = next_space;
479 next_space = str.find_first_of(
" \t", next_space);
480 words.emplace_back(str.substr(last_space, next_space - last_space));
481 next_space = str.find_first_not_of(
" \t", next_space);
482 }
while(next_space != std::string::npos);
485 }
else if(key ==
"item" || key ==
"items") {
487 std::vector<variant> items;
488 items.reserve(
split.size());
489 for(
const std::string&
s :
split) {
490 items.emplace_back(
s);
520 }
else if(key ==
"empty") {
522 }
else if(key ==
"first") {
528 }
else if(key ==
"last") {
553 for(
const auto& v :
map_) {
561 std::string key = key_variant.
as_string();
564 if(!isalpha(
c) &&
c !=
'_') {
578 const variant key_variant(key);
580 return map_[key_variant];
581 }
else if(key ==
"size") {
583 }
else if(key ==
"empty") {
642 return right_->evaluate(callable,fdb);
648 return right_->evaluate(callable,fdb);
654 return right_->evaluate(callable,fdb);
671 :
left_(std::move(left)),
key_(std::move(key))
677 s <<
left_->str() <<
'[' <<
key_->str() <<
']';
704 }
else if(op ==
"<=") {
706 }
else if(op ==
"!=") {
708 }
else if(op ==
"and") {
710 }
else if(op ==
"or") {
712 }
else if(op ==
".+") {
714 }
else if(op ==
".-") {
716 }
else if(op ==
".*") {
718 }
else if(op ==
"./") {
720 }
else if(op ==
"..") {
722 }
else if(op ==
"in") {
742 return left.
as_bool() ==
false ? left : right;
744 return left.
as_bool() ? left : right;
764 return variant(right.contains(left));
786 PLAIN_LOG <<
"ERROR: Unimplemented operator!";
794 while(faces > 0 && num_rolls-- > 0) {
802 enum OP {
AND,
OR,
NEQ,
LTE,
GTE,
OP_CAT,
OP_IN,
GT=
'>',
LT=
'<',
EQ=
'=',
RAN=
'~',
803 ADD=
'+',
SUB=
'-',
MUL=
'*',
DIV=
'/',
ADDL,
SUBL,
MULL,
DIVL,
DICE=
'd',
POW=
'^',
MOD=
'%' };
816 ,
table_(std::move(table))
830 for(expr_table::const_iterator
i =
table_->begin();
i !=
table_->end(); ++
i) {
865 for(
const expr_table::value_type &a : *
clauses_) {
866 s <<
", [" << a.first <<
"] -> ["<< a.second->str()<<
"]";
957 int bracket_depth = 0;
959 while(j !=
str.end() && (bracket_depth > 0 || *j !=
']')) {
962 }
else if(*j ==
']' && bracket_depth > 0) {
972 const std::string formula_str(
i+1, j);
973 const int pos = std::distance(
str.begin(),
i);
974 if(j -
i == 2 && (
i[1] ==
'(' ||
i[1] ==
'\'' ||
i[1] ==
')')) {
980 }
else if(*
i ==
')') {
984 i =
str.erase(
i + 1);
987 i =
str.erase(
i, j+1);
995 e.filename +=
" - string substitution";
999 subs_.push_back(sub);
1010 int j = res.size() - 1;
1012 for(
const auto& sub :
subs_) {
1013 for(; j >= sub.pos && j >= 0; j--) {
1014 if(res[j] ==
'\'') {
1015 res.replace(j, 1,
"[']");
1016 }
else if(res[j] ==
'[') {
1017 res.replace(j, 1,
"[(]");
1018 }
else if(res[j] ==
']') {
1019 res.replace(j, 1,
"[)]");
1023 const std::string
str =
"[" + sub.calculation->str() +
"]";
1024 res.insert(sub.pos,
str);
1027 for(; j >= 0; j--) {
1028 if(res[j] ==
'\'') {
1029 res.replace(j, 1,
"[']");
1030 }
else if(res[j] ==
'[') {
1031 res.replace(j, 1,
"[(]");
1032 }
else if(res[j] ==
']') {
1033 res.replace(j, 1,
"[)]");
1037 return "'" + res +
"'";
1048 for(std::size_t
i = 0;
i <
subs_.size(); ++
i) {
1049 const int j =
subs_.size() -
i - 1;
1052 const std::string
str = sub.
calculation->evaluate(variables,fdb).string_cast();
1053 res.insert(sub.
pos,
str);
1077 static std::map<std::string,int> precedence_map;
1078 if(precedence_map.empty()) {
1080 precedence_map[
"not"] = ++
n;
1081 precedence_map[
"where"] = ++
n;
1082 precedence_map[
"or"] = ++
n;
1083 precedence_map[
"and"] = ++
n;
1084 precedence_map[
"="] = ++
n;
1085 precedence_map[
"!="] =
n;
1086 precedence_map[
"<"] =
n;
1087 precedence_map[
">"] =
n;
1088 precedence_map[
"<="] =
n;
1089 precedence_map[
">="] =
n;
1090 precedence_map[
"in"] =
n;
1091 precedence_map[
"~"] = ++
n;
1092 precedence_map[
"+"] = ++
n;
1093 precedence_map[
"-"] =
n;
1094 precedence_map[
".."] =
n;
1095 precedence_map[
"*"] = ++
n;
1096 precedence_map[
"/"] =
n;
1097 precedence_map[
"%"] = ++
n;
1098 precedence_map[
"^"] = ++
n;
1099 precedence_map[
"d"] = ++
n;
1100 precedence_map[
"."] = ++
n;
1103 assert(precedence_map.count(std::string(
t.begin,
t.end)));
1104 return precedence_map[std::string(
t.begin,
t.end)];
1111 if(i1->
type == tk::token_type::lparens) {
1117 while((i1->
type != tk::token_type::rparens) && (i1 != i2)) {
1118 if(i1->
type == tk::token_type::identifier) {
1119 if(std::string((i1+1)->begin, (i1+1)->end) ==
"*") {
1120 res->push_back(std::string(i1->
begin, i1->
end) + std::string(
"*"));
1123 res->push_back(std::string(i1->
begin, i1->
end));
1125 }
else if(i1->
type == tk::token_type::comma) {
1134 if(i1->
type != tk::token_type::rparens) {
1147 if(i1->
type == tk::token_type::lparens || i1->
type == tk::token_type::lsquare ) {
1149 }
else if(i1->
type == tk::token_type::rparens || i1->
type == tk::token_type::rsquare ) {
1151 }
else if(i1->
type == tk::token_type::comma && !parens) {
1168 bool check_pointer =
false;
1172 if(i1->
type == tk::token_type::lparens || i1->
type == tk::token_type::lsquare) {
1174 }
else if(i1->
type == tk::token_type::rparens || i1->
type == tk::token_type::rsquare) {
1176 }
else if(i1->
type == tk::token_type::pointer && !parens ) {
1177 if(!check_pointer) {
1178 check_pointer =
true;
1184 }
else if(i1->
type == tk::token_type::comma && !parens ) {
1186 check_pointer =
false;
1205 const tk::token* original_i1_cached = i1;
1208 std::string var_name;
1211 if(i1->
type == tk::token_type::lparens || i1->
type == tk::token_type::lsquare) {
1213 }
else if(i1->
type == tk::token_type::rparens || i1->
type == tk::token_type::rsquare) {
1215 }
else if(!parens) {
1216 if(i1->
type == tk::token_type::comma) {
1217 if(var_name.empty()) {
1218 throw formula_error(
"There is 'where <expression>' but 'where name=<expression>' was needed",
1225 }
else if(i1->
type == tk::token_type::operator_token) {
1226 std::string op_name(i1->
begin, i1->
end);
1228 if(op_name ==
"=") {
1229 if(beg->
type != tk::token_type::identifier) {
1230 if(i1 == original_i1_cached) {
1231 throw formula_error(
"There is 'where <expression>' but 'where name=<expression>' was needed",
1234 throw formula_error(
"There is 'where <expression>=<expression>' but 'where name=<expression>' was needed",
1237 }
else if(beg+1 != i1) {
1238 throw formula_error(
"There is 'where name <expression>=<expression>' but 'where name=<expression>' was needed",
1240 }
else if(!var_name.empty()) {
1241 throw formula_error(
"There is 'where name=name=<expression>' but 'where name=<expression>' was needed",
1245 var_name.insert(var_name.end(), beg->
begin, beg->
end);
1254 if(var_name.empty()) {
1255 throw formula_error(
"There is 'where <expression>' but 'where name=<expression>' was needed",
1269 std::unique_ptr<function_symbol_table> temp_functions;
1272 symbols = temp_functions.get();
1277 if(i1->
type == tk::token_type::keyword && (i1 + 1)->type == tk::token_type::identifier) {
1278 if(std::string(i1->
begin, i1->
end) ==
"def") {
1280 const std::string formula_name = std::string(i1->
begin, i1->
end);
1282 std::vector<std::string> args;
1286 while((i1 != i2) && (i1->
type != tk::token_type::semicolon)) {
1290 const std::string precond =
"";
1291 if(symbols ==
nullptr) {
1296 std::make_shared<user_formula_function>(
1297 formula_name, std::make_shared<const formula>(beg, i1, symbols),
1302 if((i1 == i2) || (i1 == (i2-1))) {
1303 return std::make_shared<function_list_expression>(symbols);
1312 bool operator_group =
false;
1315 if(
i->type == tk::token_type::lparens ||
i->type == tk::token_type::lsquare) {
1317 }
else if(
i->type == tk::token_type::rparens ||
i->type == tk::token_type::rsquare) {
1319 }
else if(parens == 0 &&
i->type == tk::token_type::operator_token) {
1322 if(*
i->begin !=
'^' || op ==
nullptr || *op->
begin !=
'^') {
1326 operator_group =
true;
1328 operator_group =
false;
1333 if(i1->
type == tk::token_type::lparens && (i2-1)->type == tk::token_type::rparens) {
1335 }
else if((i2-1)->
type == tk::token_type::rsquare) {
1337 if(i2 - i1 == 3 && i1->
type == tk::token_type::lsquare && (i1+1)->type == tk::token_type::pointer) {
1338 return std::make_shared<map_expression>(std::vector<expression_ptr>());
1342 int square_parens = 0;
1344 while ((tok->
type != tk::token_type::lsquare || square_parens) && tok != i1) {
1345 if(tok->
type == tk::token_type::rsquare) {
1347 }
else if(tok->
type == tk::token_type::lsquare) {
1349 }
else if((tok->
type == tk::token_type::pointer) && !square_parens ) {
1355 if(tok->
type == tk::token_type::lsquare) {
1358 std::vector<expression_ptr> args;
1362 return std::make_shared<map_expression>(args);
1365 return std::make_shared<list_expression>(args);
1370 return std::make_shared<square_bracket_expression>(
1379 }
else if(i2 - i1 == 1) {
1380 if(i1->
type == tk::token_type::keyword) {
1381 if(std::string(i1->
begin, i1->
end) ==
"functions") {
1382 return std::make_shared<function_list_expression>(symbols);
1384 }
else if(i1->
type == tk::token_type::identifier) {
1385 return std::make_shared<identifier_expression>(std::string(i1->
begin, i1->
end));
1386 }
else if(i1->
type == tk::token_type::integer) {
1388 return std::make_shared<integer_expression>(
n);
1389 }
else if(i1->
type == tk::token_type::decimal) {
1391 while(*dot !=
'.') {
1399 if(literal_end - dot > 4) {
1400 literal_end = dot + 4;
1407 int multiplicator = 100;
1408 while(dot != literal_end) {
1409 f += (*dot - 48) * multiplicator;
1410 multiplicator /= 10;
1414 return std::make_shared<decimal_expression>(
n,
f);
1415 }
else if(i1->
type == tk::token_type::string_literal) {
1416 return std::make_shared<string_expression>(std::string(i1->
begin + 1, i1->
end - 1));
1418 }
else if(i1->
type == tk::token_type::identifier &&
1419 (i1+1)->type == tk::token_type::lparens &&
1420 (i2-1)->type == tk::token_type::rparens)
1422 const tk::token* function_call_begin = i1, *function_call_end = i2;
1423 int nleft = 0, nright = 0;
1425 if(
i->type == tk::token_type::lparens) {
1427 }
else if(
i->type == tk::token_type::rparens) {
1432 if(nleft == nright) {
1433 std::vector<expression_ptr> args;
1460 const std::string op_name(op->
begin,op->
end);
1462 if(op_name ==
".") {
1471 if(op_name ==
"where") {
1475 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
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