32 #define ERR_NG LOG_STREAM(err, log_engine) 43 ERR_NG <<
"Formula in WML string cannot be evaluated due to " 44 << e.
type <<
"\n\t--> \"";
59 using expr_table = std::map<std::string, expression_ptr>;
67 std::ostringstream
expr;
69 expr << std::string(i1->
begin, i1->
end) <<
" ";
81 std::string
str()
const 97 const char*
const formula::id_chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
103 , symbols_(symbols ? symbols : managed_symbols_.
get())
105 std::vector<tk::token> tokens;
106 std::string::const_iterator i1 = text.
begin(), i2 = text.
end();
109 bool fai_keyword =
false;
112 bool wfl_keyword =
false;
115 std::vector<std::pair<std::string, int>> files;
118 std::set<std::string> filenames;
120 files.emplace_back(
"formula", 1);
121 filenames.insert(
"formula");
137 std::string comment = std::string(tokens.back().begin, tokens.back().end);
138 for(
const auto& str_it : comment) {
144 files.back().second += counter;
147 files.back().second++;
149 }
else if((current_type ==
tk::TOKEN_KEYWORD) && (std::string(tokens.back().begin, tokens.back().end) ==
"fai")) {
152 }
else if((current_type ==
tk::TOKEN_KEYWORD) && (std::string(tokens.back().begin, tokens.back().end) ==
"wfl")) {
155 }
else if((current_type ==
tk::TOKEN_KEYWORD) && (std::string(tokens.back().begin, tokens.back().end) ==
"faiend")) {
156 if(files.size() > 1) {
158 filenames_it = filenames.find(files.back().first);
164 }
else if((current_type ==
tk::TOKEN_KEYWORD) && (std::string(tokens.back().begin, tokens.back().end) ==
"wflend")) {
165 if(files.size() > 1) {
167 filenames_it = filenames.find(files.back().first);
173 }
else if(fai_keyword || wfl_keyword) {
175 std::string
str = std::string(tokens.back().begin, tokens.back().end);
176 files.emplace_back(str , 1);
178 auto [pos, success] = filenames.insert(str);
184 throw formula_error(
"Faifile already included",
"fai" + str,
"", 0);
186 throw formula_error(
"Wflfile already included",
"wfl" + str,
"", 0);
195 throw formula_error(
"Expected string after the 'fai'",
"fai",
"", 0);
197 throw formula_error(
"Expected string after the 'wfl'",
"wfl",
"", 0);
202 tokens.back().filename = &(*filenames_it);
203 tokens.back().line_number = files.back().second;
208 std::string
str =
"";
209 if(!tokens.empty()) {
210 tk::token* tok_it = &tokens[0] + tokens.size()-1;
211 while(( tok_it != &tokens[0] ) && (tok_it->
line_number == tokens.back().line_number)) {
215 if(tok_it != &tokens[0] && tok_it != &tokens[0] + tokens.size() -1) {
226 if(files.size() > 1) {
227 throw formula_error(
"Missing 'wflend', make sure each .wfl file ends with it",
"",
"", 0);
230 if(!tokens.empty()) {
233 expr_ = std::make_shared<null_expression>();
246 expr_ = std::make_shared<null_expression>();
262 return expr_->evaluate(variables, fdb);
264 std::cerr <<
"formula type error: " << e.
message <<
"\n";
272 return execute(null_callable,fdb);
277 const std::string& file,
int line)
284 std::stringstream ss;
285 ss <<
"Formula error in " <<
filename <<
":" << line
286 <<
"\nIn formula " << formula
287 <<
"\nError: " <<
type;
302 virtual std::string
str()
const 304 return "{function_list_expression()}";
310 std::vector<variant> res;
311 for(
const std::string& fcn_name : symbols_->get_function_names()) {
312 res.emplace_back(fcn_name);
331 std::vector<variant> res;
332 res.reserve(items_.size());
333 for(
const auto&
i : items_) {
334 res.push_back(
i->evaluate(variables,
add_debug_info(fdb, 0,
"[list element]")));
346 bool first_item =
true;
367 virtual std::string
str()
const 371 for(std::vector<expression_ptr>::const_iterator
i = items_.begin(); (
i != items_.end()) && (
i + 1 != items_.end()) ;
i += 2) {
372 if(
i != items_.begin()) {
377 s << (*(
i+1))->str();
389 std::map<variant,variant> res;
390 for(std::vector<expression_ptr>::const_iterator
i = items_.begin(); (
i != items_.end()) && (
i + 1 != items_.end()) ;
i += 2) {
411 }
else if(op ==
"-") {
414 throw formula_error(
"Illegal unary operator: '" + op +
"'" ,
"",
"", 0);
418 virtual std::string
str()
const 421 s << op_str_ <<
'('<< operand_->str() <<
')';
451 add_input(inputs,
"size");
452 add_input(inputs,
"empty");
453 add_input(inputs,
"char");
454 add_input(inputs,
"word");
455 add_input(inputs,
"item");
461 return variant(string_.as_string().length());
462 }
else if(key ==
"empty") {
463 return variant(string_.as_string().empty());
464 }
else if(key ==
"char" || key ==
"chars") {
465 std::vector<variant> chars;
466 for(
char c : string_.as_string()) {
467 chars.emplace_back(std::string(1,
c));
471 }
else if(key ==
"word" || key ==
"words") {
472 std::vector<variant> words;
473 const std::string& str = string_.as_string();
474 std::size_t next_space = 0;
476 std::size_t last_space = next_space;
477 next_space = str.find_first_of(
" \t", next_space);
478 words.emplace_back(str.substr(last_space, next_space - last_space));
479 next_space = str.find_first_not_of(
" \t", next_space);
480 }
while(next_space != std::string::npos);
483 }
else if(key ==
"item" || key ==
"items") {
485 std::vector<variant>
items;
486 items.reserve(split.size());
487 for(
const std::string&
s : split) {
488 items.emplace_back(
s);
517 return variant(list_.num_elements());
518 }
else if(key ==
"empty") {
519 return variant(list_.num_elements() == 0);
520 }
else if(key ==
"first") {
521 if(list_.num_elements() > 0) {
526 }
else if(key ==
"last") {
527 if(list_.num_elements() > 0) {
528 return list_[list_.num_elements()-1];
551 for(
const auto& v : map_) {
559 std::string key = key_variant.
as_string();
562 if(!isalpha(
c) &&
c !=
'_') {
569 add_input(inputs, key);
576 const variant key_variant(key);
577 if(map_.as_map().find(key_variant) != map_.as_map().end()) {
578 return map_[key_variant];
579 }
else if(key ==
"size") {
580 return variant(map_.num_elements());
581 }
else if(key ==
"empty") {
582 return variant(map_.num_elements() == 0);
596 : global_(global), local_(local)
622 : left_(left), right_(right)
628 s << left_->str() <<
"." << right_->str();
640 return right_->evaluate(callable,fdb);
646 return right_->evaluate(callable,fdb);
652 return right_->evaluate(callable,fdb);
659 return right_->evaluate(callable,
add_debug_info(fdb,1,
". right"));
669 : left_(left), key_(key)
675 s << left_->str() <<
'[' << key_->str() <<
']';
698 : op_(
OP(op[0])), op_str_(op), left_(left), right_(right)
702 }
else if(op ==
"<=") {
704 }
else if(op ==
"!=") {
706 }
else if(op ==
"and") {
708 }
else if(op ==
"or") {
710 }
else if(op ==
".+") {
712 }
else if(op ==
".-") {
714 }
else if(op ==
".*") {
716 }
else if(op ==
"./") {
718 }
else if(op ==
"..") {
720 }
else if(op ==
"in") {
728 s <<
'(' << left_->str() << op_str_ << right_->str() <<
')';
762 return variant(right.contains(left));
766 return left == right ?
variant(1) : variant(0);
768 return left != right ? variant(1) : variant(0);
770 return left <= right ? variant(1) : variant(0);
772 return left >= right ? variant(1) : variant(0);
774 return left < right ? variant(1) : variant(0);
776 return left > right ? variant(1) : variant(0);
782 return variant(dice_roll(left.
as_int(), right.as_int()));
784 std::cerr <<
"ERROR: Unimplemented operator!" << std::endl;
792 while(faces > 0 && num_rolls-- > 0) {
800 enum OP { AND, OR, NEQ, LTE, GTE, OP_CAT, OP_IN, GT=
'>', LT=
'<', EQ=
'=', RAN=
'~',
801 ADD=
'+', SUB=
'-',
MUL=
'*',
DIV=
'/', ADDL,
SUBL, MULL, DIVL, DICE=
'd', POW=
'^', MOD=
'%' };
828 for(expr_table::const_iterator
i = table_->begin();
i != table_->end(); ++
i) {
829 add_input(inputs,
i->first);
836 if(i != table_->end()) {
837 expr_table_evaluated::const_iterator ev = evaluated_table_.find(key);
838 if(ev != evaluated_table_.end()) {
843 evaluated_table_[key] = v;
855 : body_(body), clauses_(clauses)
863 for(
const expr_table::value_type &
a : *clauses_) {
864 s <<
", [" <<
a.first <<
"] -> ["<<
a.second->str()<<
"]";
877 return body_->evaluate(wrapped_variables,
add_debug_info(fdb, 0,
"... where"));
954 while((i = std::find(i, str.end(),
'[')) != str.end()) {
955 int bracket_depth = 0;
957 while(j != str.end() && (bracket_depth > 0 || *j !=
']')) {
960 }
else if(*j ==
']' && bracket_depth > 0) {
970 const std::string formula_str(i+1, j);
971 const int pos = std::distance(str.begin(),
i);
972 if(j - i == 2 && (i[1] ==
'(' || i[1] ==
'\'' || i[1] ==
')')) {
978 }
else if(*i ==
')') {
982 i = str.erase(i + 1);
985 i = str.erase(i, j+1);
993 e.
filename +=
" - string substitution";
997 subs_.push_back(sub);
1007 std::string res = str_.as_string();
1008 int j = res.size() - 1;
1010 for(
const auto& sub : subs_) {
1011 for(; j >= sub.pos && j >= 0; j--) {
1012 if(res[j] ==
'\'') {
1013 res.replace(j, 1,
"[']");
1014 }
else if(res[j] ==
'[') {
1015 res.replace(j, 1,
"[(]");
1016 }
else if(res[j] ==
']') {
1017 res.replace(j, 1,
"[)]");
1021 const std::string str =
"[" + sub.calculation->str() +
"]";
1022 res.insert(sub.pos, str);
1025 for(; j >= 0; j--) {
1026 if(res[j] ==
'\'') {
1027 res.replace(j, 1,
"[']");
1028 }
else if(res[j] ==
'[') {
1029 res.replace(j, 1,
"[(]");
1030 }
else if(res[j] ==
']') {
1031 res.replace(j, 1,
"[)]");
1035 return "'" + res +
"'";
1046 for(std::size_t
i = 0;
i < subs_.size(); ++
i) {
1047 const int j = subs_.size() -
i - 1;
1050 const std::string str = sub.
calculation->evaluate(variables,fdb).string_cast();
1051 res.insert(sub.
pos, str);
1075 static std::map<std::string,int> precedence_map;
1076 if(precedence_map.empty()) {
1078 precedence_map[
"not"] = ++
n;
1079 precedence_map[
"where"] = ++
n;
1080 precedence_map[
"or"] = ++
n;
1081 precedence_map[
"and"] = ++
n;
1082 precedence_map[
"="] = ++
n;
1083 precedence_map[
"!="] =
n;
1084 precedence_map[
"<"] =
n;
1085 precedence_map[
">"] =
n;
1086 precedence_map[
"<="] =
n;
1087 precedence_map[
">="] =
n;
1088 precedence_map[
"in"] =
n;
1089 precedence_map[
"~"] = ++
n;
1090 precedence_map[
"+"] = ++
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[
"d"] = ++
n;
1098 precedence_map[
"."] = ++
n;
1101 assert(precedence_map.count(std::string(t.
begin, t.
end)));
1102 return precedence_map[std::string(t.
begin, t.
end)];
1117 if(std::string((i1+1)->begin, (i1+1)->end) ==
"*") {
1118 res->push_back(std::string(i1->
begin, i1->
end) + std::string(
"*"));
1121 res->push_back(std::string(i1->
begin, i1->
end));
1140 std::vector<expression_ptr>* res,
1164 std::vector<expression_ptr>* res,
1168 bool check_pointer =
false;
1177 if(!check_pointer) {
1178 check_pointer =
true;
1186 check_pointer =
false;
1205 const tk::token* original_i1_cached = i1;
1208 std::string var_name;
1215 }
else if(!parens) {
1217 if(var_name.empty()) {
1218 throw formula_error(
"There is 'where <expression>' but 'where name=<expression>' was needed",
1226 std::string op_name(i1->
begin, i1->
end);
1228 if(op_name ==
"=") {
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();
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;
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;
1322 if(*
i->begin !=
'^' || op ==
nullptr || *op->
begin !=
'^') {
1326 operator_group =
true;
1328 operator_group =
false;
1338 return std::make_shared<map_expression>(std::vector<expression_ptr>());
1342 int square_parens = 0;
1343 bool is_map =
false;
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) {
1381 if(std::string(i1->
begin, i1->
end) ==
"functions") {
1382 return std::make_shared<function_list_expression>(symbols);
1385 return std::make_shared<identifier_expression>(std::string(i1->
begin, i1->
end));
1387 int n = std::stoi(std::string(i1->
begin, i1->
end));
1388 return std::make_shared<integer_expression>(
n);
1391 while(*dot !=
'.') {
1395 int n = std::stoi(std::string(i1->
begin,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);
1416 return std::make_shared<string_expression>(std::string(i1->
begin + 1, i1->
end - 1));
1422 const tk::token* function_call_begin = i1, *function_call_end = i2;
1423 int nleft = 0, nright = 0;
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);
square_bracket_expression(expression_ptr left, expression_ptr key)
variant get_value(const std::string &key) const
TOKEN_TYPE
TOKEN_TYPE is already defined in a Winnt.h (a windows header which is included under some conditions...
variant execute(const formula_callable &, formula_debugger *) const
void get_inputs(formula_input_vector &inputs) const
string_callable(const variant &string)
variant get_value(const std::string &key) const
std::vector< expression_ptr > items_
integer_expression(int i)
std::string evaluate_formula_impl(const std::string &)
static l_noret error(LoadState *S, const char *why)
formula_debugger * add_debug_info(formula_debugger *fdb, int arg_number, const std::string &f_name)
variant execute(const formula_callable &, formula_debugger *) const
static void body(LexState *ls, expdesc *e, int ismethod, int line)
std::vector< substitution > subs_
std::map< std::string, variant > expr_table_evaluated
variant execute(const formula_callable &variables, formula_debugger *fdb) const
std::vector< formula_input > formula_input_vector
map_expression(const std::vector< expression_ptr > &items)
formula_debugger * debugger_
const formula_callable & local_
variant execute(const formula_callable &variables, formula_debugger *fdb) const
virtual std::string str() const
virtual std::string str() const
const std::vector< std::string > items
list_expression(const std::vector< expression_ptr > &items)
std::shared_ptr< formula_expression > expression_ptr
variant execute(const formula_callable &variables, formula_debugger *fdb) const
variant get_value(const std::string &key) const
variant execute(const formula_callable &variables, formula_debugger *fdb) const
variant get_value(const std::string &key) const
const std::string & as_string() const
static void parse_where_clauses(const tk::token *i1, const tk::token *i2, expr_table_ptr res, function_symbol_table *symbols)
virtual std::string str() const
variant execute(const formula_callable &variables, formula_debugger *fdb) const
std::vector< expression_ptr > items_
struct utils::detail::formula_initer init
unary_operator_expression(const std::string &op, expression_ptr arg)
where_expression(expression_ptr body, expr_table_ptr clauses)
list_callable(const variant &list)
void get_inputs(formula_input_vector &inputs) const
where_variables(const formula_callable &base, expr_table_ptr table, formula_debugger *fdb)
variant execute(const formula_callable &variables, formula_debugger *fdb) const
variant list_elements_add(const variant &v) const
static std::shared_ptr< function_symbol_table > get_builtins()
variant execute(const formula_callable &variables, formula_debugger *fdb) const
static int operator_precedence(const tk::token &t)
Functions to handle the actual parsing of WFL.
variant list_elements_mul(const variant &v) const
decimal_expression(int i, int f)
variant execute(const formula_callable &, formula_debugger *) const
std::string(* evaluate_formula)(const std::string &formula)
variant build_range(const variant &v) const
std::map< std::string, expression_ptr > expr_table
std::string string_cast() const
const_formula_callable_ptr as_callable() const
variant execute(const formula_callable &variables, formula_debugger *) const
const std::string * filename
variant list_elements_div(const variant &v) const
void get_inputs(formula_input_vector &inputs) const
void get_inputs(formula_input_vector &inputs) const
operator_expression(const std::string &op, expression_ptr left, expression_ptr right)
token get_token(iterator &i1, const iterator i2)
function_list_expression(function_symbol_table *symbols)
static void expr(LexState *ls, expdesc *v)
static map_location::DIRECTION s
const formula_callable & base_
int get_random_int(int min, int max)
This helper method provides a random int from the underlying generator, using results of next_random...
std::shared_ptr< const formula > const_formula_ptr
void get_inputs(formula_input_vector &inputs) const
rng * generator
This generator is automatically synced during synced context.
static int dice_roll(int num_rolls, int faces)
static void parse_set_args(const tk::token *i1, const tk::token *i2, std::vector< expression_ptr > *res, function_symbol_table *symbols)
string_expression(std::string str)
variant execute(const formula_callable &, formula_debugger *) const
static std::string tokens_to_string(const tk::token *i1, const tk::token *i2)
variant concatenate(const variant &v) const
std::vector< std::string > split(const config_attribute_value &val)
static void parse_args(const tk::token *i1, const tk::token *i2, std::vector< expression_ptr > *res, function_symbol_table *symbols)
expression_ptr create_function(const std::string &fn, const std::vector< expression_ptr > &args) const
Standard logging facilities (interface).
expr_table_evaluated evaluated_table_
void add_function(const std::string &name, formula_function_ptr &&fcn)
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Classes that encapsulate and handle the various formula functions.
static void reverse(lua_State *L, StkId from, StkId to)
const_formula_ptr calculation
std::shared_ptr< expr_table > expr_table_ptr
variant get_member(const std::string &name) const
function_symbol_table * symbols_
dot_callable(const formula_callable &global, const formula_callable &local)
variant get_value(const std::string &key) const
bool as_bool() const
Returns a boolean state of the variant value.
dot_expression(expression_ptr left, expression_ptr right)
expression_ptr parse_expression(const tk::token *i1, const tk::token *i2, function_symbol_table *symbols)
static map_location::DIRECTION n
map_callable(const variant &map)
static void parse_function_args(const tk::token *&i1, const tk::token *i2, std::vector< std::string > *res)
std::string::const_iterator iterator
identifier_expression(const std::string &id)
variant list_elements_sub(const variant &v) const
std::vector< std::string > parenthetical_split(const std::string &val, const char separator, const std::string &left, const std::string &right, const int flags)
Splits a string based either on a separator, except then the text appears within specified parenthesi...
std::shared_ptr< formula > formula_ptr