New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
implemented parsing (string -> Basic) #772
Changes from 8 commits
6d3b995
04844a5
679369d
b4a9ff5
ef3a3cc
3e32442
4be43a1
9a504db
9f6401d
d5bc2f3
e224483
3724fa8
a3eb2be
4808233
b6c83a7
472cd35
184a679
0c64af9
ad37dcd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,6 +39,7 @@ set(SRC | |
expression.cpp | ||
numer_denom.cpp | ||
derivative.cpp | ||
parser.cpp | ||
) | ||
|
||
if (WITH_MPFR) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
#include <symengine/basic.h> | ||
#include <symengine/symbol.h> | ||
#include <symengine/add.h> | ||
#include <symengine/integer.h> | ||
#include <symengine/rational.h> | ||
#include <symengine/complex.h> | ||
#include <symengine/mul.h> | ||
#include <symengine/pow.h> | ||
#include <symengine/functions.h> | ||
#include <symengine/constants.h> | ||
#include <symengine/visitor.h> | ||
#include <vector> | ||
#include <stack> | ||
#include <map> | ||
#include <set> | ||
#include <string> | ||
#include <functional> | ||
namespace SymEngine { | ||
|
||
class ExpressionParser | ||
{ | ||
std::set<char> OPERATORS = {'-', '+', '*', '/', '^', '(', ')', ','}; | ||
std::map<char, int> op_precedence = { | ||
{')', 0}, {',', 0}, {'-', 1}, {'+', 1}, | ||
{'*', 3}, {'/', 4}, {'^', 5} | ||
}; | ||
std::map<std::string, RCP<const Basic> > constants = { | ||
{"e", E}, {"EulerGamma", EulerGamma}, {"pi", pi} | ||
}; | ||
|
||
// reference : http://stackoverflow.com/questions/30393285/stdfunction-fails-to-distinguish-overloaded-functions | ||
typedef RCP<const Basic> (*single_arg_func)(const RCP<const Basic>&); | ||
typedef RCP<const Basic> (*double_arg_func)(const RCP<const Basic>&, const RCP<const Basic>&); | ||
|
||
single_arg_func single_casted_log = log; // as they are overloaded | ||
single_arg_func single_casted_zeta = zeta; | ||
|
||
std::map< std::string, | ||
std::function<RCP<const Basic>(const RCP<const Basic>&)> | ||
> single_arg_functions = { | ||
|
||
{"sin", sin}, {"cos", cos}, {"tan", tan}, | ||
{"cot", cot}, {"csc", csc}, {"sec", sec}, | ||
|
||
{"arcsin", asin}, {"arccos", acos}, {"arctan", atan}, | ||
{"arcsec", asec}, {"arccsc", acsc}, {"arccot", acot}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep the original name. i.e. |
||
|
||
{"sinh", sinh}, {"cosh", cosh}, {"tanh", tanh}, | ||
{"coth", coth}, // implement sech, csch | ||
|
||
{"arcsinh", asinh}, {"arccosh", acosh}, {"arctanh", atanh}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here |
||
{"arcsech", asech}, {"arccoth", acoth}, // implement acsch | ||
|
||
{"gamma", gamma}, {"sqrt", sqrt}, {"abs", abs}, {"exp", exp}, | ||
{"lambertw", lambertw}, {"dirichlet_eta", dirichlet_eta}, | ||
{"ln", single_casted_log}, {"log", single_casted_log}, {"zeta", single_casted_zeta} | ||
}; | ||
|
||
double_arg_func double_casted_log = log; // as they are overloaded | ||
double_arg_func double_casted_zeta = zeta; | ||
|
||
std::map< std::string, | ||
std::function<RCP<const Basic>(const RCP<const Basic>&, const RCP<const Basic>&)> | ||
> double_arg_functions = { | ||
|
||
{"log", double_casted_log}, {"zeta", double_casted_zeta}, | ||
{"pow", pow}, {"beta", beta}, {"uppergamma", uppergamma}, | ||
{"lowergamma", lowergamma}, {"kronecker_delta", kronecker_delta}, | ||
{"polygamma", polygamma} | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All these can probably be made |
||
|
||
std::vector<int> operator_end; | ||
std::string s; | ||
unsigned int s_len; | ||
|
||
RCP<const Basic> parse_string(unsigned int l, unsigned int h) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you think of any case where it might be useful to expose this by making |
||
{ | ||
RCP<const Basic> result; | ||
bool result_set = false; | ||
bool is_not_numeric = false; | ||
std::string expr = ""; | ||
|
||
for (unsigned int iter = l; iter < h; ++iter) | ||
{ | ||
if (is_operator(iter)) | ||
{ | ||
if (s[iter] != '(') | ||
if (!result_set) | ||
result = set_result(expr, is_not_numeric); | ||
|
||
switch(s[iter]) | ||
{ | ||
case '+': | ||
result = add(result, parse_string(iter+1, operator_end[iter])); | ||
iter = operator_end[iter]-1; | ||
break; | ||
case '*': | ||
result = mul(result, parse_string(iter+1, operator_end[iter])); | ||
iter = operator_end[iter]-1; | ||
break; | ||
case '-': | ||
result = sub(result, parse_string(iter+1, operator_end[iter])); | ||
iter = operator_end[iter]-1; | ||
break; | ||
case '/': | ||
result = div(result, parse_string(iter+1, operator_end[iter])); | ||
iter = operator_end[iter]-1; | ||
break; | ||
case '^': | ||
result = pow(result, parse_string(iter+1, operator_end[iter])); | ||
iter = operator_end[iter]-1; | ||
break; | ||
case '(': | ||
result = functionify(iter, expr); | ||
break; | ||
case ')': | ||
continue; | ||
case ',': | ||
continue; | ||
} | ||
|
||
result_set = true; | ||
is_not_numeric = false; | ||
} | ||
else | ||
{ | ||
expr += s[iter]; | ||
|
||
int ascii = s[iter] - '0'; | ||
if (ascii < 0 or ascii > 9) | ||
is_not_numeric = true; | ||
|
||
if (iter == h-1) | ||
result = set_result(expr, is_not_numeric); | ||
} | ||
} | ||
|
||
return result; | ||
} | ||
|
||
bool is_operator(int iter) | ||
{ | ||
if (iter >= 0 and iter < (int)s_len) | ||
if (OPERATORS.find(s[iter]) != OPERATORS.end()) | ||
return true; | ||
return false; | ||
} | ||
|
||
RCP<const Basic> functionify(unsigned int& iter, std::string expr) | ||
{ | ||
RCP<const Basic> param1 = parse_string(iter+1, operator_end[iter]); | ||
iter = operator_end[iter] - 1; | ||
|
||
if(expr == "") return param1; | ||
|
||
if (single_arg_functions.find(expr) != single_arg_functions.end()) | ||
if (s[iter+1] != ',') | ||
return single_arg_functions[expr](param1); | ||
|
||
iter++; | ||
RCP<const Basic> param2 = parse_string(iter+1, operator_end[iter]); | ||
iter = operator_end[iter] - 1; | ||
|
||
if (double_arg_functions.find(expr) != double_arg_functions.end()) | ||
return double_arg_functions[expr](param1, param2); | ||
|
||
throw std::runtime_error("Unknown function " + expr); | ||
// remaining : levi_civita | ||
} | ||
|
||
RCP<const Basic> set_result(std::string &expr, bool& is_not_numeric) | ||
{ | ||
if (expr == "") return zero; | ||
|
||
if (is_not_numeric) | ||
{ | ||
if (constants.find(expr) != constants.end()) | ||
return constants[expr]; | ||
return symbol(expr); | ||
} | ||
else | ||
return integer(std::atoi(expr.c_str())); | ||
} | ||
|
||
public: | ||
|
||
RCP<const Basic> parse(std::string &in) | ||
{ | ||
std::stack<unsigned int> right_bracket; | ||
std::stack<std::pair<int, unsigned int> > op_stack; | ||
s = ""; | ||
|
||
for (unsigned int i = 0; i < in.length(); ++i) | ||
{ | ||
if (in[i] == ' ') | ||
continue; | ||
s += in[i]; | ||
} | ||
|
||
s_len = s.length(); | ||
operator_end.clear(); | ||
operator_end.resize(s_len); | ||
op_stack.push(std::make_pair(-1, s_len)); | ||
|
||
for (int i = s_len-1; i >= 0; i--) | ||
{ | ||
if (is_operator(i)) | ||
{ | ||
char x = s[i]; | ||
if(x == '(') | ||
{ | ||
while(op_stack.top().second != right_bracket.top()) | ||
op_stack.pop(); | ||
operator_end[i] = right_bracket.top(); | ||
right_bracket.pop(); | ||
op_stack.pop(); | ||
} | ||
else if(x == ')' or x == ',') | ||
{ | ||
if (x == ',') | ||
{ | ||
operator_end[i] = right_bracket.top(); | ||
right_bracket.pop(); | ||
} | ||
op_stack.push(std::make_pair(op_precedence[x], i)); | ||
right_bracket.push(i); | ||
} | ||
else | ||
{ | ||
while(op_precedence[x] < op_stack.top().first) | ||
op_stack.pop(); | ||
|
||
operator_end[i] = op_stack.top().second; | ||
op_stack.push(std::make_pair(op_precedence[x], i)); | ||
} | ||
} | ||
} | ||
return parse_string(0, s_len); | ||
} | ||
}; | ||
|
||
} // SymEngine |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add
I
as a constant too. Then complex numbers will be supported as well.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh didn't think about that! Thanks!