14 #include <boost/lexical_cast.hpp>
27 const char*
const formula::id_chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
31 std::cerr <<
"ERROR: cannot set key '" << key <<
"' on object" << std::endl;
50 std::map<std::string,variant>::const_iterator it =
values_.find(key);
65 for(std::map<std::string,variant>::const_iterator
i =
values_.begin();
i !=
values_.end(); ++
i) {
85 return "{function_list_expression()}";
88 variant execute(
const formula_callable& , formula_debugger * )
const {
89 std::vector<variant>
res;
91 std::vector<std::string> more_function_names =
symbols_->get_function_names();
92 function_names.insert(function_names.end(), more_function_names.begin(), more_function_names.end());
93 for(
size_t i = 0;
i < function_names.size();
i++) {
94 res.push_back(
variant(function_names[
i]));
102 class list_expression :
public formula_expression {
104 explicit list_expression(
const std::vector<expression_ptr>&
items)
109 variant execute(
const formula_callable& variables, formula_debugger *fdb)
const {
110 std::vector<variant>
res;
111 res.reserve(
items_.size());
112 for(std::vector<expression_ptr>::const_iterator
i =
items_.begin();
i !=
items_.end(); ++
i) {
113 res.push_back((*i)->evaluate(variables,
add_debug_info(fdb, 0,
"[list element]")));
125 bool first_item =
true;
141 class map_expression :
public formula_expression {
143 explicit map_expression(
const std::vector<expression_ptr>& items)
151 for(std::vector<expression_ptr>::const_iterator
i =
items_.begin(); (
i !=
items_.end() ) && (
i+1 !=
items_.end() ) ;
i+=2) {
157 s << (*(
i+1))->str();
166 variant execute(
const formula_callable& variables, formula_debugger *fdb)
const {
167 std::map<variant,variant>
res;
168 for(std::vector<expression_ptr>::const_iterator
i =
items_.begin(); (
i !=
items_.end() ) && (
i+1 !=
items_.end() ) ;
i+=2) {
177 std::vector<expression_ptr>
items_;
180 class unary_operator_expression :
public formula_expression {
188 }
else if(op ==
"-") {
191 throw formula_error(
"Illegal unary operator: '" + op +
"'" ,
"",
"", 0);
202 variant execute(
const formula_callable& variables, formula_debugger *fdb)
const {
212 enum OP {
NOT, SUB };
218 class string_callable :
public formula_callable {
221 explicit string_callable(
const variant&
string) : string_(string)
224 void get_inputs(std::vector<formula_input>* inputs)
const {
235 }
else if(key ==
"empty") {
237 }
else if(key ==
"char" || key ==
"chars") {
238 std::vector<variant> chars;
243 }
else if(key ==
"word" || key ==
"words") {
244 std::vector<variant> words;
246 size_t next_space = 0;
248 size_t last_space = next_space;
249 next_space = str.find_first_of(
" \t", next_space);
250 words.push_back(
variant(str.substr(last_space, next_space - last_space)));
251 next_space = str.find_first_not_of(
" \t", next_space);
252 }
while(next_space != std::string::npos);
254 }
else if(key ==
"item" || key ==
"items") {
256 std::vector<variant>
items;
257 items.reserve(split.size());
268 class list_callable :
public formula_callable {
271 explicit list_callable(
const variant& list) : list_(list)
274 void get_inputs(std::vector<formula_input>* inputs)
const {
284 }
else if(key ==
"empty") {
286 }
else if(key ==
"first") {
292 }
else if(key ==
"last") {
305 class map_callable :
public formula_callable {
308 explicit map_callable(
const variant& map) : map_(map)
311 void get_inputs(std::vector<formula_input>* inputs)
const {
324 if(!isalpha(
c) &&
c !=
'_') {
336 const variant key_variant(key);
337 if(map_.
as_map().find(key_variant) != map_.
as_map().end()) {
338 return map_[key_variant];
339 }
else if(key ==
"size") {
341 }
else if(key ==
"empty") {
349 class dot_callable :
public formula_callable {
351 dot_callable(
const formula_callable &global,
352 const formula_callable& local)
357 void get_inputs(std::vector<formula_input>* inputs)
const {
358 return local_.get_inputs(inputs);
362 variant v = local_.query_value(key);
365 return global_.query_value(key);
371 class dot_expression :
public formula_expression {
383 variant execute(
const formula_callable& variables, formula_debugger *fdb)
const {
387 list_callable list_call(left);
388 dot_callable callable(variables, list_call);
389 return right_->evaluate(callable,fdb);
392 map_callable map_call(left);
393 dot_callable callable(variables, map_call);
394 return right_->evaluate(callable,fdb);
397 string_callable string_call(left);
398 dot_callable callable(variables, string_call);
399 return right_->evaluate(callable,fdb);
405 dot_callable callable(variables, *left.
as_callable());
412 class square_bracket_expression :
public formula_expression {
421 s <<
left_->str() <<
'[' <<
key_->str() <<
']';
425 variant execute(
const formula_callable& variables, formula_debugger *fdb)
const {
438 class operator_expression :
public formula_expression {
446 }
else if(op ==
"<=") {
448 }
else if(op ==
"!=") {
450 }
else if(op ==
"and") {
452 }
else if(op ==
"or") {
454 }
else if(op ==
".+") {
456 }
else if(op ==
".-") {
458 }
else if(op ==
".*") {
460 }
else if(op ==
"./") {
462 }
else if(op ==
"..") {
464 }
else if(op ==
"in") {
476 variant execute(
const formula_callable& variables, formula_debugger *fdb)
const {
503 return variant(right.contains(left));
507 return left == right ?
variant(1) : variant(0);
509 return left != right ? variant(1) : variant(0);
511 return left <= right ? variant(1) : variant(0);
513 return left >= right ? variant(1) : variant(0);
515 return left < right ? variant(1) : variant(0);
517 return left > right ? variant(1) : variant(0);
523 return variant(dice_roll(left.
as_int(), right.as_int()));
525 std::cerr <<
"ERROR: Unimplemented operator!" << std::endl;
530 static int dice_roll(
int num_rolls,
int faces) {
532 while(faces > 0 && num_rolls-- > 0) {
536 res += (rand() % faces) + 1;
543 enum OP { AND, OR, NEQ, LTE, GTE, OP_CAT, OP_IN, GT=
'>', LT=
'<', EQ=
'=', RAN=
'~',
544 ADD=
'+', SUB=
'-',
MUL=
'*',
DIV=
'/', ADDL, SUBL, MULL, DIVL, DICE=
'd', POW=
'^', MOD=
'%' };
551 typedef std::map<std::string,expression_ptr> expr_table;
553 typedef std::map<std::string, variant> exp_table_evaluated;
555 class where_variables:
public formula_callable {
557 where_variables(
const formula_callable &base,
558 expr_table_ptr
table, formula_debugger* fdb )
559 : formula_callable(false)
573 void get_inputs(std::vector<formula_input>* inputs)
const {
574 for(expr_table::const_iterator
i = table_->begin();
i != table_->end(); ++
i) {
581 if(i != table_->end()) {
582 exp_table_evaluated::const_iterator ev = evaluated_table_.find(key);
583 if( ev != evaluated_table_.end())
586 variant v = i->second->evaluate(base_,
add_debug_info(debugger_, 0,
"where[" + key +
"]"));
587 evaluated_table_[key] =
v;
590 return base_.query_value(key);
594 class where_expression:
public formula_expression {
597 expr_table_ptr clauses)
606 for(
const expr_table::value_type &
a : *
clauses_) {
607 s <<
", [" <<
a.first <<
"] -> ["<<
a.second->str()<<
"]";
617 variant execute(
const formula_callable& variables,formula_debugger *fdb)
const {
618 where_variables wrapped_variables(variables, clauses_, fdb);
619 return body_->evaluate(wrapped_variables,
add_debug_info(fdb, 0,
"... where"));
624 class identifier_expression :
public formula_expression {
633 variant execute(
const formula_callable& variables, formula_debugger * )
const {
634 return variables.query_value(
id_);
639 class null_expression :
public formula_expression {
641 explicit null_expression() {}
646 variant execute(
const formula_callable& , formula_debugger * )
const {
652 class integer_expression :
public formula_expression {
654 explicit integer_expression(
int i) :
i_(i)
663 variant execute(
const formula_callable& , formula_debugger * )
const {
670 class decimal_expression :
public formula_expression {
672 explicit decimal_expression(
int i,
int f) :
i_(i),
f_(f)
685 variant execute(
const formula_callable& , formula_debugger * )
const {
692 class string_expression :
public formula_expression {
699 while((i =
std::find(i, str.end(),
'[')) != str.end()) {
700 int bracket_depth = 0;
702 while(j != str.end() && (bracket_depth > 0 || *j !=
']')) {
705 }
else if(*j ==
']' && bracket_depth > 0) {
715 const int pos = i - str.begin();
716 if(j - i == 2 && (i[1] ==
'(' || i[1] ==
'\'' || i[1] ==
')')) {
720 if(*i ==
'(') *i =
'[';
721 else if(*i ==
')') *i =
']';
722 i = str.erase(i + 1);
725 i = str.erase(i, j+1);
731 sub.calculation.reset(
new formula(formula_str));
732 }
catch(formula_error&
e) {
733 e.filename +=
" - string substitution";
736 subs_.push_back(sub);
747 int j = res.size() - 1;
748 for(
size_t i = 0; i <
subs_.size(); ++
i) {
750 for(;j >= sub.pos && j >= 0;j--) {
752 res.replace(j, 1,
"[']");
753 }
else if(res[j] ==
'[') {
754 res.replace(j, 1,
"[(]");
755 }
else if(res[j] ==
']') {
756 res.replace(j, 1,
"[)]");
759 const std::string str =
"[" + sub.calculation->str() +
"]";
760 res.insert(sub.pos, str);
764 res.replace(j, 1,
"[']");
765 }
else if(res[j] ==
'[') {
766 res.replace(j, 1,
"[(]");
767 }
else if(res[j] ==
']') {
768 res.replace(j, 1,
"[)]");
772 return "'" + res +
"'";
775 variant execute(
const formula_callable& variables, formula_debugger *fdb)
const {
780 for(
size_t i=0; i <
subs_.size(); ++
i) {
781 const int j =
subs_.size() - i - 1;
782 const substitution& sub =
subs_[
i];
784 const std::string str = sub.calculation->evaluate(variables,fdb).string_cast();
785 res.insert(sub.pos, str);
792 struct substitution {
809 int operator_precedence(
const token&
t)
811 static std::map<std::string,int> precedence_map;
812 if(precedence_map.empty()) {
814 precedence_map[
"not"] = ++
n;
815 precedence_map[
"where"] = ++
n;
816 precedence_map[
"or"] = ++
n;
817 precedence_map[
"and"] = ++
n;
818 precedence_map[
"="] = ++
n;
819 precedence_map[
"!="] =
n;
820 precedence_map[
"<"] =
n;
821 precedence_map[
">"] =
n;
822 precedence_map[
"<="] =
n;
823 precedence_map[
">="] =
n;
824 precedence_map[
"in"] =
n;
825 precedence_map[
"~"] = ++
n;
826 precedence_map[
"+"] = ++
n;
827 precedence_map[
"-"] =
n;
828 precedence_map[
".."] =
n;
829 precedence_map[
"*"] = ++
n;
830 precedence_map[
"/"] =
n;
831 precedence_map[
"%"] = ++
n;
832 precedence_map[
"^"] = ++
n;
833 precedence_map[
"d"] = ++
n;
834 precedence_map[
"."] = ++
n;
847 std::ostringstream
expr;
855 void parse_function_args(
const token* &i1,
const token* i2,
856 std::vector<std::string>* res)
863 throw formula_error(
"Invalid function definition", tokens_to_string(begin,end-1), *i1->
filename, i1->
line_number);
868 if(
std::string((i1+1)->begin, (i1+1)->end) ==
"*") {
877 throw formula_error(
"Invalid function definition", tokens_to_string(begin,end-1), *i1->
filename, i1->
line_number);
883 throw formula_error(
"Invalid function definition", tokens_to_string(begin,end-1), *i1->
filename, i1->
line_number);
888 void parse_args(
const token* i1,
const token* i2,
889 std::vector<expression_ptr>* res,
890 function_symbol_table* symbols)
893 const token* beg = i1;
900 res->push_back(parse_expression(beg,i1, symbols));
907 res->push_back(parse_expression(beg,i1, symbols));
911 void parse_set_args(
const token* i1,
const token* i2,
912 std::vector<expression_ptr>* res,
913 function_symbol_table* symbols)
916 bool check_pointer =
false;
917 const token* beg = i1;
918 const token* begin = i1, *end = i2;
925 if (!check_pointer) {
926 check_pointer =
true;
927 res->push_back(parse_expression(beg,i1, symbols));
930 throw formula_error(
"Too many '->' operators found", tokens_to_string(begin,end-1), *i1->
filename, i1->
line_number);
934 check_pointer =
false;
936 throw formula_error(
"Expected comma, but '->' found", tokens_to_string(begin,end-1), *i1->
filename, i1->
line_number);
938 res->push_back(parse_expression(beg,i1, symbols));
946 res->push_back(parse_expression(beg,i1, symbols));
950 void parse_where_clauses(
const token* i1,
const token * i2,
951 expr_table_ptr res, function_symbol_table* symbols) {
953 const token *original_i1_cached = i1;
954 const token *beg = i1;
955 const token* begin = i1, *end = i2;
964 if(var_name.empty()) {
965 throw formula_error(
"There is 'where <expression>' but 'where name=<expression>' was needed", tokens_to_string(begin,end-1), *i1->
filename, i1->
line_number);
967 (*res)[var_name] = parse_expression(beg,i1, symbols);
974 if(i1 == original_i1_cached) {
975 throw formula_error(
"There is 'where <expression' but 'where name=<expression>' was needed", tokens_to_string(begin,end-1), *i1->
filename, i1->
line_number);
977 throw formula_error(
"There is 'where <expression>=<expression>' but 'where name=<expression>' was needed", tokens_to_string(begin,end-1), *i1->
filename, i1->
line_number);
979 }
else if(beg+1 != i1) {
980 throw formula_error(
"There is 'where name <expression>=<expression>' but 'where name=<expression>' was needed", tokens_to_string(begin,end-1), *i1->
filename, i1->
line_number);
981 }
else if(!var_name.empty()) {
982 throw formula_error(
"There is 'where name=name=<expression>' but 'where name=<expression>' was needed", tokens_to_string(begin,end-1), *i1->
filename, i1->
line_number);
984 var_name.insert(var_name.end(), beg->
begin, beg->
end);
992 if(var_name.empty()) {
993 throw formula_error(
"There is 'where <expression>' but 'where name=<expression>' was needed", tokens_to_string(begin,end-1), *i1->
filename, i1->
line_number);
995 (*res)[var_name] = parse_expression(beg,i1, symbols);
1005 const token* begin = i1, *end = i2;
1012 std::vector<std::string> args;
1013 parse_function_args(++i1, i2, &args);
1014 const token* beg = i1;
1019 if(symbols ==
nullptr) {
1020 throw formula_error(
"Function symbol table required but not present",
"",*i1->
filename, i1->
line_number);
1022 symbols->add_function(formula_name,
1027 if((i1 == i2) || (i1 == (i2-1))) {
1031 return parse_expression((i1+1), i2, symbols);
1037 const token* op =
nullptr;
1038 bool operator_group =
false;
1039 for(
const token* i = i1; i != i2; ++
i) {
1045 if( ( !operator_group ) && (op ==
nullptr || operator_precedence(*op) >=
1046 operator_precedence(*i)) ) {
1048 if(*i->begin !=
'^' || op ==
nullptr || *op->
begin !=
'^')
1051 operator_group =
true;
1053 operator_group =
false;
1059 return parse_expression(i1+1,i2-1,symbols);
1063 return expression_ptr(
new map_expression(std::vector<expression_ptr>()));
1065 const token* tok = i2-2;
1066 int square_parens = 0;
1067 bool is_map =
false;
1081 std::vector<expression_ptr> args;
1084 parse_set_args(i1+1, i2-1, &args, symbols);
1087 parse_args(i1+1,i2-1,&args,symbols);
1094 parse_expression(i1,tok,symbols),
1095 parse_expression(tok+1,i2-1,symbols)));
1097 catch (formula_error&
e){
1098 throw formula_error( e.type, tokens_to_string(i1, i2-1), *i1->
filename, i1->
line_number );
1102 }
else if(i2 - i1 == 1) {
1115 while( *dot !=
'.' )
1129 int multiplicator = 100;
1130 while( dot != end) {
1131 f += (*dot - 48)*multiplicator;
1132 multiplicator /= 10;
1143 const token* begin = i1, *end = i2;
1144 int nleft = 0, nright = 0;
1145 for(
const token* i = i1; i != i2; ++
i) {
1153 if(nleft == nright) {
1154 std::vector<expression_ptr> args;
1155 parse_args(i1+2,i2-1,&args,symbols);
1159 catch(formula_error&
e) {
1165 throw formula_error(
"Could not parse expression", tokens_to_string(i1, i2), *i1->
filename, i1->
line_number);
1167 if (op + 1 == end) {
1168 throw formula_error(
"Expected another token", tokens_to_string(begin,end-1), *op->
filename, op->
line_number);
1174 parse_expression(op+1,i2,symbols)));
1176 catch(formula_error&
e) {
1177 throw formula_error( e.type, tokens_to_string(begin,end-1), *op->
filename, op->
line_number);
1183 if(op_name ==
".") {
1185 parse_expression(i1,op,symbols),
1186 parse_expression(op+1,i2,symbols)));
1189 if(op_name ==
"where") {
1190 expr_table_ptr
table(
new expr_table());
1191 parse_where_clauses(op+1, i2,
table, symbols);
1192 return expression_ptr(
new where_expression(parse_expression(i1, op, symbols),
1197 op_name, parse_expression(i1,op,symbols),
1198 parse_expression(op+1,i2,symbols)));
1223 managing_symbols(symbols == nullptr)
1227 if(symbols ==
nullptr) {
1231 std::vector<token> tokens;
1232 std::string::const_iterator i1 = str.
begin(), i2 = str.
end();
1235 bool fai_keyword =
false;
1237 bool wfl_keyword =
false;
1239 std::vector< std::pair< std::string, int> > files;
1241 std::set< std::string > filenames;
1242 files.push_back( std::make_pair(
"formula", 1 ) );
1243 filenames.insert(
"formula" );
1261 if( *str_it ==
'\n' )
1268 files.back().second++;
1280 if (files.size() > 1) {
1282 filenames_it = filenames.find( files.back().first );
1285 throw formula_error(
"Unexpected 'faiend' found",
"",
"", 0);
1289 if (files.size() > 1) {
1291 filenames_it = filenames.find( files.back().first );
1294 throw formula_error(
"Unexpected 'wflend' found",
"",
"", 0);
1297 if (fai_keyword || wfl_keyword) {
1300 files.push_back( std::make_pair( str , 1 ) );
1302 ret = filenames.insert( str );
1303 if(ret.second==
true) {
1304 filenames_it = ret.first;
1307 throw formula_error(
"Faifile already included",
"fai" + str,
"", 0);
1308 else throw formula_error(
"Wflfile already included",
"wfl" + str,
"", 0);
1311 fai_keyword =
false;
1312 wfl_keyword =
false;
1315 throw formula_error(
"Expected string after the 'fai'",
"fai",
"", 0);
1316 else throw formula_error(
"Expected string after the 'wfl'",
"wfl",
"", 0);
1320 tokens.back().filename = &(*filenames_it);
1321 tokens.back().line_number = files.back().second;
1326 if(!tokens.empty()) {
1327 token* tok_it = &tokens[0] + tokens.size()-1;
1328 while( ( tok_it != &tokens[0] ) && (tok_it->
line_number == tokens.back().line_number) )
1331 if( tok_it != &tokens[0] && tok_it != &tokens[0] + tokens.size() -1)
1334 str = tokens_to_string( tok_it, &tokens[0] + tokens.size() );
1341 if(files.size() > 1) {
1342 throw formula_error(
"Missing 'wflend', make sure each .wfl file ends with it",
"",
"", 0);
1345 if(!tokens.empty()) {
1346 expr_ = parse_expression(&tokens[0],&tokens[0] + tokens.size(),
symbols_);
1358 if(symbols ==
nullptr) {
1363 expr_ = parse_expression(i1,i2, symbols);
1372 return expr_->evaluate(variables, fdb);
1374 std::cerr <<
"formula type error: " << e.
message <<
"\n";
1382 return execute(null_callable,fdb);
1393 std::stringstream ss;
1394 ss <<
"Formula error in " <<
filename <<
":" << line
1395 <<
"\nIn formula " << formula
1396 <<
"\nError: " <<
type;
size_t num_elements() const
variant build_range(const variant &v) const
boost::shared_ptr< formula > formula_ptr
rng * generator
This generator is automatically synced during synced context.
GLuint GLuint GLsizei GLenum type
static l_noret error(LoadState *S, const char *why)
static void body(LexState *ls, expdesc *e, int ismethod, int line)
const game_logic::formula_callable * as_callable() const
boost::shared_ptr< const formula > const_formula_ptr
expression_ptr create_function(const std::string &fn, const std::vector< expression_ptr > &args, const function_symbol_table *symbols)
variant_iterator end() const
const std::map< variant, variant > & as_map() const
const std::vector< std::string > items
variant list_elements_sub(const variant &v) const
variant list_elements_mul(const variant &v) const
variant list_elements_add(const variant &v) const
std::vector< std::string > builtin_function_names()
static std::string sub(const std::string &s)
Private function to surround an argument with brackets.
uint32_t next_random()
Provides the next random draw.
GLsizei const GLfloat * value
variant list_elements_div(const variant &v) const
formula_debugger * add_debug_info(formula_debugger *fdb, int arg_number, const std::string &f_name)
GLboolean GLboolean GLboolean GLboolean a
Iterator class for the variant.
variant concatenate(const variant &v) const
variant_iterator begin() const
boost::shared_ptr< formula_expression > expression_ptr
static void expr(LexState *ls, expdesc *v)
const std::string & as_string() const
const formula_callable * fallback_
std::vector< std::string > parenthetical_split(std::string const &val, const char separator, std::string const &left, std::string const &right, const int flags)
Splits a string based either on a separator where text within parenthesis is protected from splitting...
bool find(E event, F functor)
Tests whether an event handler is available.
boost::shared_ptr< formula_function > formula_function_ptr
variant get_member(const std::string &str) const
GLsizei GLenum GLuint GLuint GLsizei char * message
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
GLenum GLsizei GLenum GLenum const GLvoid * table
GLsizei const GLcharARB ** string
const std::string valid
Little parts of regex templates used to parse Wml annoations.