33 #define ERR_NG LOG_STREAM(err, log_engine)
44 ERR_NG <<
"Formula in WML string cannot be evaluated due to "
45 <<
e.type <<
"\n\t--> \"";
60 using expr_table = std::map<std::string, expression_ptr>;
68 std::ostringstream
expr;
82 std::string
str()
const
98 const char*
const formula::id_chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
104 , symbols_(symbols ? symbols : managed_symbols_.
get())
106 std::vector<tk::token> tokens;
107 std::string::const_iterator i1 = text.begin(), i2 = text.end();
110 bool fai_keyword =
false;
113 bool wfl_keyword =
false;
116 std::vector<std::pair<std::string, int>> files;
119 std::set<std::string> filenames;
121 files.emplace_back(
"formula", 1);
122 filenames.insert(
"formula");
132 if(current_type == tk::token_type::whitespace) {
134 }
else if(current_type == tk::token_type::comment) {
138 std::string comment = std::string(tokens.back().begin, tokens.back().end);
139 for(
const auto& str_it : comment) {
145 files.back().second += counter;
147 }
else if(current_type == tk::token_type::eol) {
148 files.back().second++;
150 }
else if((current_type == tk::token_type::keyword) && (std::string(tokens.back().begin, tokens.back().end) ==
"fai")) {
153 }
else if((current_type == tk::token_type::keyword) && (std::string(tokens.back().begin, tokens.back().end) ==
"wfl")) {
156 }
else if((current_type == tk::token_type::keyword) && (std::string(tokens.back().begin, tokens.back().end) ==
"faiend")) {
157 if(files.size() > 1) {
159 filenames_it = filenames.find(files.back().first);
165 }
else if((current_type == tk::token_type::keyword) && (std::string(tokens.back().begin, tokens.back().end) ==
"wflend")) {
166 if(files.size() > 1) {
168 filenames_it = filenames.find(files.back().first);
174 }
else if(fai_keyword || wfl_keyword) {
175 if(current_type == tk::token_type::string_literal) {
176 std::string
str = std::string(tokens.back().begin, tokens.back().end);
177 files.emplace_back(
str , 1);
179 auto [pos, success] = filenames.insert(
str);
196 throw formula_error(
"Expected string after the 'fai'",
"fai",
"", 0);
198 throw formula_error(
"Expected string after the 'wfl'",
"wfl",
"", 0);
203 tokens.back().filename = &(*filenames_it);
204 tokens.back().line_number = files.back().second;
209 std::string
str =
"";
210 if(!tokens.empty()) {
211 tk::token* tok_it = &tokens[0] + tokens.size()-1;
212 while(( tok_it != &tokens[0] ) && (tok_it->
line_number == tokens.back().line_number)) {
216 if(tok_it != &tokens[0] && tok_it != &tokens[0] + tokens.size() -1) {
223 throw formula_error(
e.description_,
str +
e.formula_, *filenames_it, files.back().second);
227 if(files.size() > 1) {
228 throw formula_error(
"Missing 'wflend', make sure each .wfl file ends with it",
"",
"", 0);
231 if(!tokens.empty()) {
234 expr_ = std::make_shared<null_expression>();
242 , symbols_(symbols ? symbols : managed_symbols_.
get())
247 expr_ = std::make_shared<null_expression>();
263 return expr_->evaluate(variables, fdb);
265 PLAIN_LOG <<
"formula type error: " <<
e.message;
273 return execute(null_callable,fdb);
278 const std::string& file,
int line)
285 std::stringstream ss;
288 <<
"\nError: " <<
type;
303 virtual std::string
str()
const
305 return "{function_list_expression()}";
311 std::vector<variant> res;
313 res.emplace_back(fcn_name);
332 std::vector<variant> res;
333 res.reserve(
items_.size());
335 res.push_back(
i->evaluate(variables,
add_debug_info(fdb, 0,
"[list element]")));
347 bool first_item =
true;
368 virtual std::string
str()
const
372 for(std::vector<expression_ptr>::const_iterator
i =
items_.begin(); (
i !=
items_.end()) && (
i + 1 !=
items_.end()) ;
i += 2) {
378 s << (*(
i+1))->str();
390 std::map<variant,variant> res;
391 for(std::vector<expression_ptr>::const_iterator
i =
items_.begin(); (
i !=
items_.end()) && (
i + 1 !=
items_.end()) ;
i += 2) {
412 }
else if(op ==
"-") {
415 throw formula_error(
"Illegal unary operator: '" + op +
"'" ,
"",
"", 0);
419 virtual std::string
str()
const
463 }
else if(key ==
"empty") {
465 }
else if(key ==
"char" || key ==
"chars") {
466 std::vector<variant> chars;
468 chars.emplace_back(std::string(1,
c));
472 }
else if(key ==
"word" || key ==
"words") {
473 std::vector<variant> words;
475 std::size_t next_space = 0;
477 std::size_t last_space = next_space;
478 next_space = str.find_first_of(
" \t", next_space);
479 words.emplace_back(str.substr(last_space, next_space - last_space));
480 next_space = str.find_first_not_of(
" \t", next_space);
481 }
while(next_space != std::string::npos);
484 }
else if(key ==
"item" || key ==
"items") {
486 std::vector<variant> items;
487 items.reserve(
split.size());
488 for(
const std::string&
s :
split) {
489 items.emplace_back(
s);
519 }
else if(key ==
"empty") {
521 }
else if(key ==
"first") {
527 }
else if(key ==
"last") {
552 for(
const auto& v :
map_) {
560 std::string key = key_variant.
as_string();
563 if(!isalpha(
c) &&
c !=
'_') {
577 const variant key_variant(key);
579 return map_[key_variant];
580 }
else if(key ==
"size") {
582 }
else if(key ==
"empty") {
641 return right_->evaluate(callable,fdb);
647 return right_->evaluate(callable,fdb);
653 return right_->evaluate(callable,fdb);
676 s <<
left_->str() <<
'[' <<
key_->str() <<
']';
703 }
else if(op ==
"<=") {
705 }
else if(op ==
"!=") {
707 }
else if(op ==
"and") {
709 }
else if(op ==
"or") {
711 }
else if(op ==
".+") {
713 }
else if(op ==
".-") {
715 }
else if(op ==
".*") {
717 }
else if(op ==
"./") {
719 }
else if(op ==
"..") {
721 }
else if(op ==
"in") {
741 return left.
as_bool() ==
false ? left : right;
743 return left.
as_bool() ? left : right;
763 return variant(right.contains(left));
785 PLAIN_LOG <<
"ERROR: Unimplemented operator!";
793 while(faces > 0 && num_rolls-- > 0) {
801 enum OP {
AND,
OR,
NEQ,
LTE,
GTE,
OP_CAT,
OP_IN,
GT=
'>',
LT=
'<',
EQ=
'=',
RAN=
'~',
802 ADD=
'+',
SUB=
'-',
MUL=
'*',
DIV=
'/',
ADDL,
SUBL,
MULL,
DIVL,
DICE=
'd',
POW=
'^',
MOD=
'%' };
829 for(expr_table::const_iterator
i =
table_->begin();
i !=
table_->end(); ++
i) {
864 for(
const expr_table::value_type &a : *
clauses_) {
865 s <<
", [" << a.first <<
"] -> ["<< a.second->str()<<
"]";
955 while((
i = std::find(
i,
str.end(),
'[')) !=
str.end()) {
956 int bracket_depth = 0;
958 while(j !=
str.end() && (bracket_depth > 0 || *j !=
']')) {
961 }
else if(*j ==
']' && bracket_depth > 0) {
971 const std::string formula_str(
i+1, j);
972 const int pos = std::distance(
str.begin(),
i);
973 if(j -
i == 2 && (
i[1] ==
'(' ||
i[1] ==
'\'' ||
i[1] ==
')')) {
979 }
else if(*
i ==
')') {
983 i =
str.erase(
i + 1);
986 i =
str.erase(
i, j+1);
994 e.filename +=
" - string substitution";
998 subs_.push_back(sub);
1009 int j = res.size() - 1;
1011 for(
const auto& sub :
subs_) {
1012 for(; j >= sub.pos && j >= 0; j--) {
1013 if(res[j] ==
'\'') {
1014 res.replace(j, 1,
"[']");
1015 }
else if(res[j] ==
'[') {
1016 res.replace(j, 1,
"[(]");
1017 }
else if(res[j] ==
']') {
1018 res.replace(j, 1,
"[)]");
1022 const std::string
str =
"[" + sub.calculation->str() +
"]";
1023 res.insert(sub.pos,
str);
1026 for(; j >= 0; j--) {
1027 if(res[j] ==
'\'') {
1028 res.replace(j, 1,
"[']");
1029 }
else if(res[j] ==
'[') {
1030 res.replace(j, 1,
"[(]");
1031 }
else if(res[j] ==
']') {
1032 res.replace(j, 1,
"[)]");
1036 return "'" + res +
"'";
1047 for(std::size_t
i = 0;
i <
subs_.size(); ++
i) {
1048 const int j =
subs_.size() -
i - 1;
1051 const std::string
str = sub.
calculation->evaluate(variables,fdb).string_cast();
1052 res.insert(sub.
pos,
str);
1076 static std::map<std::string,int> precedence_map;
1077 if(precedence_map.empty()) {
1079 precedence_map[
"not"] = ++
n;
1080 precedence_map[
"where"] = ++
n;
1081 precedence_map[
"or"] = ++
n;
1082 precedence_map[
"and"] = ++
n;
1083 precedence_map[
"="] = ++
n;
1084 precedence_map[
"!="] =
n;
1085 precedence_map[
"<"] =
n;
1086 precedence_map[
">"] =
n;
1087 precedence_map[
"<="] =
n;
1088 precedence_map[
">="] =
n;
1089 precedence_map[
"in"] =
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[
"^"] = ++
n;
1098 precedence_map[
"d"] = ++
n;
1099 precedence_map[
"."] = ++
n;
1102 assert(precedence_map.count(std::string(
t.begin,
t.end)));
1103 return precedence_map[std::string(
t.begin,
t.end)];
1110 if(i1->
type == tk::token_type::lparens) {
1116 while((i1->
type != tk::token_type::rparens) && (i1 != i2)) {
1117 if(i1->
type == tk::token_type::identifier) {
1118 if(std::string((i1+1)->begin, (i1+1)->end) ==
"*") {
1119 res->push_back(std::string(i1->
begin, i1->
end) + std::string(
"*"));
1122 res->push_back(std::string(i1->
begin, i1->
end));
1124 }
else if(i1->
type == tk::token_type::comma) {
1133 if(i1->
type != tk::token_type::rparens) {
1146 if(i1->
type == tk::token_type::lparens || i1->
type == tk::token_type::lsquare ) {
1148 }
else if(i1->
type == tk::token_type::rparens || i1->
type == tk::token_type::rsquare ) {
1150 }
else if(i1->
type == tk::token_type::comma && !parens) {
1167 bool check_pointer =
false;
1171 if(i1->
type == tk::token_type::lparens || i1->
type == tk::token_type::lsquare) {
1173 }
else if(i1->
type == tk::token_type::rparens || i1->
type == tk::token_type::rsquare) {
1175 }
else if(i1->
type == tk::token_type::pointer && !parens ) {
1176 if(!check_pointer) {
1177 check_pointer =
true;
1183 }
else if(i1->
type == tk::token_type::comma && !parens ) {
1185 check_pointer =
false;
1204 const tk::token* original_i1_cached = i1;
1207 std::string var_name;
1210 if(i1->
type == tk::token_type::lparens || i1->
type == tk::token_type::lsquare) {
1212 }
else if(i1->
type == tk::token_type::rparens || i1->
type == tk::token_type::rsquare) {
1214 }
else if(!parens) {
1215 if(i1->
type == tk::token_type::comma) {
1216 if(var_name.empty()) {
1217 throw formula_error(
"There is 'where <expression>' but 'where name=<expression>' was needed",
1224 }
else if(i1->
type == tk::token_type::operator_token) {
1225 std::string op_name(i1->
begin, i1->
end);
1227 if(op_name ==
"=") {
1228 if(beg->
type != tk::token_type::identifier) {
1229 if(i1 == original_i1_cached) {
1230 throw formula_error(
"There is 'where <expression>' but 'where name=<expression>' was needed",
1233 throw formula_error(
"There is 'where <expression>=<expression>' but 'where name=<expression>' was needed",
1236 }
else if(beg+1 != i1) {
1237 throw formula_error(
"There is 'where name <expression>=<expression>' but 'where name=<expression>' was needed",
1239 }
else if(!var_name.empty()) {
1240 throw formula_error(
"There is 'where name=name=<expression>' but 'where name=<expression>' was needed",
1244 var_name.insert(var_name.end(), beg->
begin, beg->
end);
1253 if(var_name.empty()) {
1254 throw formula_error(
"There is 'where <expression>' but 'where name=<expression>' was needed",
1268 std::unique_ptr<function_symbol_table> temp_functions;
1271 symbols = temp_functions.get();
1276 if(i1->
type == tk::token_type::keyword && (i1 + 1)->type == tk::token_type::identifier) {
1277 if(std::string(i1->
begin, i1->
end) ==
"def") {
1279 const std::string formula_name = std::string(i1->
begin, i1->
end);
1281 std::vector<std::string> args;
1285 while((i1 != i2) && (i1->
type != tk::token_type::semicolon)) {
1289 const std::string precond =
"";
1290 if(symbols ==
nullptr) {
1295 std::make_shared<user_formula_function>(
1296 formula_name, std::make_shared<const formula>(beg, i1, symbols),
1301 if((i1 == i2) || (i1 == (i2-1))) {
1302 return std::make_shared<function_list_expression>(symbols);
1311 bool operator_group =
false;
1314 if(
i->type == tk::token_type::lparens ||
i->type == tk::token_type::lsquare) {
1316 }
else if(
i->type == tk::token_type::rparens ||
i->type == tk::token_type::rsquare) {
1318 }
else if(parens == 0 &&
i->type == tk::token_type::operator_token) {
1321 if(*
i->begin !=
'^' || op ==
nullptr || *op->
begin !=
'^') {
1325 operator_group =
true;
1327 operator_group =
false;
1332 if(i1->
type == tk::token_type::lparens && (i2-1)->type == tk::token_type::rparens) {
1334 }
else if((i2-1)->
type == tk::token_type::rsquare) {
1336 if(i2 - i1 == 3 && i1->
type == tk::token_type::lsquare && (i1+1)->type == tk::token_type::pointer) {
1337 return std::make_shared<map_expression>(std::vector<expression_ptr>());
1341 int square_parens = 0;
1343 while ((tok->
type != tk::token_type::lsquare || square_parens) && tok != i1) {
1344 if(tok->
type == tk::token_type::rsquare) {
1346 }
else if(tok->
type == tk::token_type::lsquare) {
1348 }
else if((tok->
type == tk::token_type::pointer) && !square_parens ) {
1354 if(tok->
type == tk::token_type::lsquare) {
1357 std::vector<expression_ptr> args;
1361 return std::make_shared<map_expression>(args);
1364 return std::make_shared<list_expression>(args);
1369 return std::make_shared<square_bracket_expression>(
1378 }
else if(i2 - i1 == 1) {
1379 if(i1->
type == tk::token_type::keyword) {
1380 if(std::string(i1->
begin, i1->
end) ==
"functions") {
1381 return std::make_shared<function_list_expression>(symbols);
1383 }
else if(i1->
type == tk::token_type::identifier) {
1384 return std::make_shared<identifier_expression>(std::string(i1->
begin, i1->
end));
1385 }
else if(i1->
type == tk::token_type::integer) {
1386 int n = std::stoi(std::string(i1->
begin, i1->
end));
1387 return std::make_shared<integer_expression>(
n);
1388 }
else if(i1->
type == tk::token_type::decimal) {
1390 while(*dot !=
'.') {
1394 int n = std::stoi(std::string(i1->
begin,dot));
1398 if(literal_end - dot > 4) {
1399 literal_end = dot + 4;
1406 int multiplicator = 100;
1407 while(dot != literal_end) {
1408 f += (*dot - 48) * multiplicator;
1409 multiplicator /= 10;
1413 return std::make_shared<decimal_expression>(
n,
f);
1414 }
else if(i1->
type == tk::token_type::string_literal) {
1415 return std::make_shared<string_expression>(std::string(i1->
begin + 1, i1->
end - 1));
1417 }
else if(i1->
type == tk::token_type::identifier &&
1418 (i1+1)->type == tk::token_type::lparens &&
1419 (i2-1)->type == tk::token_type::rparens)
1421 const tk::token* function_call_begin = i1, *function_call_end = i2;
1422 int nleft = 0, nright = 0;
1424 if(
i->type == tk::token_type::lparens) {
1426 }
else if(
i->type == tk::token_type::rparens) {
1431 if(nleft == nright) {
1432 std::vector<expression_ptr> args;
1459 const std::string op_name(op->
begin,op->
end);
1461 if(op_name ==
".") {
1470 if(op_name ==
"where") {
1474 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 &)
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)
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_where_clauses(const tk::token *i1, const tk::token *i2, expr_table_ptr res, function_symbol_table *symbols)
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_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