Skip to content

Commit dc8243b

Browse files
authored
Parser: Support comparison chain (#4556)
Parser now supports comparison chain such as `a <= b <= c`.
1 parent 6a975de commit dc8243b

File tree

10 files changed

+659
-383
lines changed

10 files changed

+659
-383
lines changed

Docs/sphinx_documentation/source/Basics.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,8 @@ Complete elliptic integrals of the first and second kind, ``comp_ellint_1(k)`` a
651651
are supported.
652652
There is ``if(a,b,c)`` that gives ``b`` or ``c`` depending on the value of
653653
``a``. A number of comparison operators are supported, including ``<``,
654-
``>``, ``==``, ``!=``, ``<=``, and ``>=``. The Boolean results from
654+
``>``, ``==``, ``!=``, ``<=``, and ``>=``, and they can be chained.
655+
The Boolean results from
655656
comparison can be combined by ``and`` and ``or``, and they hold the value ``1``
656657
for true and ``0`` for false. The precedence of the operators follows the
657658
convention of the C and C++ programming languages. Here is an example of using
@@ -661,7 +662,7 @@ the parser.
661662
662663
::
663664

664-
Parser parser("if(x>a and x<b, sin(x)*cos(y)*if(z<0, 1.0, exp(-z)), .3*c**2)");
665+
Parser parser("if(a<x<b, sin(x)*cos(y)*if(z<0, 1.0, exp(-z)), .3*c**2)");
665666
parser.setConstant("a", ...);
666667
parser.setConstant("b", ...);
667668
parser.setConstant("c", ...);

Src/Base/Parser/AMReX_IParser_Y.H

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ enum iparser_f2_t { // Built-in functions with two arguments
3232
IPARSER_LEQ,
3333
IPARSER_EQ,
3434
IPARSER_NEQ,
35+
IPARSER_CMP_CHAIN,
3536
IPARSER_AND,
3637
IPARSER_OR,
3738
IPARSER_MIN,
@@ -143,6 +144,8 @@ struct iparser_node* iparser_newf3 (enum iparser_f3_t ftype, struct iparser_node
143144
struct iparser_node* n2, struct iparser_node* n3);
144145
struct iparser_node* iparser_newassign (struct iparser_symbol* s, struct iparser_node* v);
145146
struct iparser_node* iparser_newlist (struct iparser_node* nl, struct iparser_node* nr);
147+
struct iparser_node* iparser_newcmpchain (struct iparser_node* nl, enum iparser_f2_t cmp,
148+
struct iparser_node* nr);
146149

147150
/*******************************************************************/
148151

@@ -229,6 +232,7 @@ iparser_call_f2 (enum iparser_f2_t type, long long a, long long b)
229232
return (a == b) ? 1 : 0;
230233
case IPARSER_NEQ:
231234
return (a != b) ? 1 : 0;
235+
case IPARSER_CMP_CHAIN:
232236
case IPARSER_AND:
233237
return ((a != 0) && (b != 0)) ? 1 : 0;
234238
case IPARSER_OR:

Src/Base/Parser/AMReX_IParser_Y.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,50 @@ iparser_newlist (struct iparser_node* nl, struct iparser_node* nr)
133133
}
134134
}
135135

136+
namespace {
137+
struct iparser_node* iparser_get_rightmost_operand (struct iparser_node* node)
138+
{
139+
if (node && node->type == IPARSER_F2) {
140+
auto ftype = ((struct iparser_f2*)node)->ftype;
141+
if (ftype == IPARSER_CMP_CHAIN) {
142+
return iparser_get_rightmost_operand(node->r);
143+
} else if (ftype == IPARSER_LT || ftype == IPARSER_GT ||
144+
ftype == IPARSER_LEQ || ftype == IPARSER_GEQ ||
145+
ftype == IPARSER_EQ || ftype == IPARSER_NEQ) {
146+
return node->r;
147+
}
148+
}
149+
return nullptr;
150+
}
151+
152+
bool iparser_is_comparison (struct iparser_node* node)
153+
{
154+
if (node && node->type == IPARSER_F2) {
155+
auto ftype = ((struct iparser_f2*)node)->ftype;
156+
return (ftype == IPARSER_LT || ftype == IPARSER_GT ||
157+
ftype == IPARSER_LEQ || ftype == IPARSER_GEQ ||
158+
ftype == IPARSER_EQ || ftype == IPARSER_NEQ ||
159+
(ftype == IPARSER_CMP_CHAIN && iparser_is_comparison(node->r)));
160+
} else {
161+
return false;
162+
}
163+
}
164+
}
165+
166+
struct iparser_node* iparser_newcmpchain (struct iparser_node* nl, enum iparser_f2_t cmp,
167+
struct iparser_node* nr)
168+
{
169+
/* If left side is already a comparison, this extends the chain */
170+
if (amrex::iparser_is_comparison(nl)) {
171+
return amrex::iparser_newf2(amrex::IPARSER_CMP_CHAIN, nl,
172+
amrex::iparser_newf2(cmp,
173+
amrex::iparser_get_rightmost_operand(nl),
174+
nr));
175+
} else {
176+
return amrex::iparser_newf2(cmp, nl, nr); // Initial comparison
177+
}
178+
}
179+
136180
/*******************************************************************/
137181

138182
struct amrex_iparser*
@@ -884,6 +928,9 @@ iparser_ast_optimize (struct iparser_node* node)
884928
case IPARSER_F2:
885929
iparser_ast_optimize(node->l);
886930
iparser_ast_optimize(node->r);
931+
if (((struct iparser_f2*)node)->ftype == IPARSER_CMP_CHAIN) {
932+
((struct iparser_f2*)node)->ftype = IPARSER_AND;
933+
}
887934
if (node->l->type == IPARSER_NUMBER &&
888935
node->r->type == IPARSER_NUMBER)
889936
{
@@ -1021,6 +1068,7 @@ iparser_ast_print_f2 (struct iparser_f2* f2, std::string const& space, AllPrint&
10211068
case IPARSER_NEQ:
10221069
printer << "NEQ\n";
10231070
break;
1071+
case IPARSER_CMP_CHAIN:
10241072
case IPARSER_AND:
10251073
printer << "AND\n";
10261074
break;

Src/Base/Parser/AMReX_Parser_Y.H

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ enum parser_f2_t { // Built-in functions with two arguments
9494
PARSER_LEQ,
9595
PARSER_EQ,
9696
PARSER_NEQ,
97+
PARSER_CMP_CHAIN,
9798
PARSER_AND,
9899
PARSER_OR,
99100
PARSER_HEAVISIDE,
@@ -121,6 +122,7 @@ std::string_view parser_f2_s[] =
121122
"eq",
122123
"neq",
123124
"and",
125+
"and",
124126
"or",
125127
"heaviside",
126128
"jn",
@@ -293,6 +295,8 @@ struct parser_node* parser_newusrfn (struct parser_symbol* fname,
293295
std::vector<struct parser_node*> const& nv);
294296
struct parser_node* parser_newassign (struct parser_symbol* s, struct parser_node* v);
295297
struct parser_node* parser_newlist (struct parser_node* nl, struct parser_node* nr);
298+
struct parser_node* parser_newcmpchain (struct parser_node* nl, enum parser_f2_t cmp,
299+
struct parser_node* nr);
296300

297301
/*******************************************************************/
298302

@@ -519,6 +523,7 @@ parser_call_f2 (enum parser_f2_t type, double a, double b)
519523
return (a == b) ? 1.0 : 0.0;
520524
case PARSER_NEQ:
521525
return (a != b) ? 1.0 : 0.0;
526+
case PARSER_CMP_CHAIN:
522527
case PARSER_AND:
523528
return ((a != 0.0) && (b != 0.0)) ? 1.0 : 0.0;
524529
case PARSER_OR:

Src/Base/Parser/AMReX_Parser_Y.cpp

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,50 @@ parser_newlist (struct parser_node* nl, struct parser_node* nr)
198198
}
199199
}
200200

201+
namespace {
202+
struct parser_node* parser_get_rightmost_operand (struct parser_node* node)
203+
{
204+
if (node && node->type == PARSER_F2) {
205+
auto ftype = ((struct parser_f2*)node)->ftype;
206+
if (ftype == PARSER_CMP_CHAIN) {
207+
return parser_get_rightmost_operand(node->r);
208+
} else if (ftype == PARSER_LT || ftype == PARSER_GT ||
209+
ftype == PARSER_LEQ || ftype == PARSER_GEQ ||
210+
ftype == PARSER_EQ || ftype == PARSER_NEQ) {
211+
return node->r;
212+
}
213+
}
214+
return nullptr;
215+
}
216+
217+
bool parser_is_comparison (struct parser_node* node)
218+
{
219+
if (node && node->type == PARSER_F2) {
220+
auto ftype = ((struct parser_f2*)node)->ftype;
221+
return (ftype == PARSER_LT || ftype == PARSER_GT ||
222+
ftype == PARSER_LEQ || ftype == PARSER_GEQ ||
223+
ftype == PARSER_EQ || ftype == PARSER_NEQ ||
224+
(ftype == PARSER_CMP_CHAIN && parser_is_comparison(node->r)));
225+
} else {
226+
return false;
227+
}
228+
}
229+
}
230+
231+
struct parser_node* parser_newcmpchain (struct parser_node* nl, enum parser_f2_t cmp,
232+
struct parser_node* nr)
233+
{
234+
/* If left side is already a comparison, this extends the chain */
235+
if (amrex::parser_is_comparison(nl)) {
236+
return amrex::parser_newf2(amrex::PARSER_CMP_CHAIN, nl,
237+
amrex::parser_newf2(cmp,
238+
amrex::parser_get_rightmost_operand(nl),
239+
nr));
240+
} else {
241+
return amrex::parser_newf2(cmp, nl, nr); // Initial comparison
242+
}
243+
}
244+
201245
/*******************************************************************/
202246

203247
struct amrex_parser*
@@ -1324,6 +1368,9 @@ parser_ast_optimize (struct parser_node*& node, std::map<std::string,double>& lo
13241368
case PARSER_F2:
13251369
parser_ast_optimize(((struct parser_f2*)node)->l,local_consts);
13261370
parser_ast_optimize(((struct parser_f2*)node)->r,local_consts);
1371+
if (((struct parser_f2*)node)->ftype == PARSER_CMP_CHAIN) {
1372+
((struct parser_f2*)node)->ftype = PARSER_AND;
1373+
}
13271374
if (((struct parser_f2*)node)->l->type == PARSER_NUMBER &&
13281375
((struct parser_f2*)node)->r->type == PARSER_NUMBER)
13291376
{
@@ -1333,6 +1380,54 @@ parser_ast_optimize (struct parser_node*& node, std::map<std::string,double>& lo
13331380
((struct parser_number*)(((struct parser_f2*)node)->r))->value);
13341381
parser_set_number(node, v);
13351382
}
1383+
else if (((struct parser_f2*)node)->ftype == PARSER_AND &&
1384+
((struct parser_f2*)node)->r->type == PARSER_NUMBER &&
1385+
parser_get_number(((struct parser_f2*)node)->r) == 0.0)
1386+
{ // ? and false => false
1387+
parser_set_number(node, 0.0);
1388+
}
1389+
else if (((struct parser_f2*)node)->ftype == PARSER_AND &&
1390+
((struct parser_f2*)node)->r->type == PARSER_NUMBER &&
1391+
parser_get_number(((struct parser_f2*)node)->r) != 0.0)
1392+
{ // ? and true => ?
1393+
std::memcpy(node, node->l, sizeof(struct parser_node));
1394+
}
1395+
else if (((struct parser_f2*)node)->ftype == PARSER_AND &&
1396+
((struct parser_f2*)node)->l->type == PARSER_NUMBER &&
1397+
parser_get_number(((struct parser_f2*)node)->l) == 0.0)
1398+
{ // false and ? => false
1399+
parser_set_number(node, 0.0);
1400+
}
1401+
else if (((struct parser_f2*)node)->ftype == PARSER_AND &&
1402+
((struct parser_f2*)node)->l->type == PARSER_NUMBER &&
1403+
parser_get_number(((struct parser_f2*)node)->l) != 0.0)
1404+
{ // true and ? => ?
1405+
std::memcpy(node, node->r, sizeof(struct parser_node));
1406+
}
1407+
else if (((struct parser_f2*)node)->ftype == PARSER_OR &&
1408+
((struct parser_f2*)node)->r->type == PARSER_NUMBER &&
1409+
parser_get_number(((struct parser_f2*)node)->r) != 0.0)
1410+
{ // ? or true => true
1411+
parser_set_number(node, 1.0);
1412+
}
1413+
else if (((struct parser_f2*)node)->ftype == PARSER_OR &&
1414+
((struct parser_f2*)node)->r->type == PARSER_NUMBER &&
1415+
parser_get_number(((struct parser_f2*)node)->r) == 0.0)
1416+
{ // ? or false => ?
1417+
std::memcpy(node, node->l, sizeof(struct parser_node));
1418+
}
1419+
else if (((struct parser_f2*)node)->ftype == PARSER_OR &&
1420+
((struct parser_f2*)node)->l->type == PARSER_NUMBER &&
1421+
parser_get_number(((struct parser_f2*)node)->l) != 0.0)
1422+
{ // true or ? => true
1423+
parser_set_number(node, 1.0);
1424+
}
1425+
else if (((struct parser_f2*)node)->ftype == PARSER_OR &&
1426+
((struct parser_f2*)node)->l->type == PARSER_NUMBER &&
1427+
parser_get_number(((struct parser_f2*)node)->l) == 0.0)
1428+
{ // false or ? => ?
1429+
std::memcpy(node, node->r, sizeof(struct parser_node));
1430+
}
13361431
else if (((struct parser_f2*)node)->ftype == PARSER_POW &&
13371432
((struct parser_f2*)node)->r->type == PARSER_NUMBER &&
13381433
parser_get_number(((struct parser_f2*)node)->r) == 0.0)

0 commit comments

Comments
 (0)