Skip to content

Commit 010504c

Browse files
committed
Parser: Support comparison chain
Parser now supports comparison chain such as `a <= b <= c`. Sync w/ amrex AMReX-Codes/amrex#4556.
1 parent feaebf7 commit 010504c

File tree

6 files changed

+360
-177
lines changed

6 files changed

+360
-177
lines changed

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ can be computed with `min` and `max`, respectively. It supports the
2121
Heaviside step function, `heaviside(x1,x2)` that gives `0`, `x2`, `1`, for
2222
`x1 < 0`, `x1 = 0` and `x1 > 0`, respectively. It supports the Bessel
2323
function of the first kind of order `n` `jn(n,x)`, and the Bessel function
24-
of the second kind of order ``n`` ``yn(n,x)``. Complete elliptic
25-
integrals of the first and second kind, `comp_ellint_1(k)` and
26-
`comp_ellint_2(k)`, are supported. There is `if(a,b,c)` that gives `b` or
27-
`c` depending on the value of `a`. A number of comparison operators are
28-
supported, including `<`, `>`, `==`, `!=`, `<=`, and `>=`. The Boolean
24+
of the second kind of order ``n`` ``yn(n,x)``. Complete elliptic integrals
25+
of the first and second kind, `comp_ellint_1(k)` and `comp_ellint_2(k)`, are
26+
supported. There is `if(a,b,c)` that gives `b` or `c` depending on the
27+
value of `a`. A number of comparison operators are supported, including
28+
`<`, `>`, `==`, `!=`, `<=`, and `>=`, and they can be chained. The Boolean
2929
results from comparison can be combined by `and` and `or`, and they hold the
3030
value `1` for true and `0` for false. The precedence of the operators
3131
follows the convention of the C and C++ programming languages. Here is an
@@ -34,7 +34,7 @@ example of using the parser.
3434
```c++
3535
#include "amrexpr.hpp"
3636

37-
Parser parser("if(x>a and x<b, sin(x)*cos(y)*if(z<0, 1.0, exp(-z)), .3*c**2)");
37+
Parser parser("if(a<x<b, sin(x)*cos(y)*if(z<0, 1.0, exp(-z)), .3*c**2)");
3838
parser.setConstant(a, ...);
3939
parser.setConstant(b, ...);
4040
parser.setConstant(c, ...);

Src/amrexpr_Parser_Y.H

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ enum parser_f2_t { // Built-in functions with two arguments
8787
PARSER_LEQ,
8888
PARSER_EQ,
8989
PARSER_NEQ,
90+
PARSER_CMP_CHAIN,
9091
PARSER_AND,
9192
PARSER_OR,
9293
PARSER_HEAVISIDE,
@@ -114,6 +115,7 @@ std::string_view parser_f2_s[] =
114115
"eq",
115116
"neq",
116117
"and",
118+
"and",
117119
"or",
118120
"heaviside",
119121
"jn",
@@ -247,6 +249,8 @@ struct parser_node* parser_newf3 (enum parser_f3_t ftype, struct parser_node* n1
247249
struct parser_node* n2, struct parser_node* n3);
248250
struct parser_node* parser_newassign (struct parser_symbol* s, struct parser_node* v);
249251
struct parser_node* parser_newlist (struct parser_node* nl, struct parser_node* nr);
252+
struct parser_node* parser_newcmpchain (struct parser_node* nl, enum parser_f2_t cmp,
253+
struct parser_node* nr);
250254

251255
/*******************************************************************/
252256

@@ -470,6 +474,7 @@ parser_call_f2 (enum parser_f2_t type, double a, double b)
470474
return (a == b) ? 1.0 : 0.0;
471475
case PARSER_NEQ:
472476
return (a != b) ? 1.0 : 0.0;
477+
case PARSER_CMP_CHAIN:
473478
case PARSER_AND:
474479
return ((a != 0.0) && (b != 0.0)) ? 1.0 : 0.0;
475480
case PARSER_OR:

Src/amrexpr_Parser_Y.cpp

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,50 @@ parser_newlist (struct parser_node* nl, struct parser_node* nr)
157157
}
158158
}
159159

160+
namespace {
161+
struct parser_node* parser_get_rightmost_operand (struct parser_node* node)
162+
{
163+
if (node && node->type == PARSER_F2) {
164+
auto ftype = ((struct parser_f2*)node)->ftype;
165+
if (ftype == PARSER_CMP_CHAIN) {
166+
return parser_get_rightmost_operand(node->r);
167+
} else if (ftype == PARSER_LT || ftype == PARSER_GT ||
168+
ftype == PARSER_LEQ || ftype == PARSER_GEQ ||
169+
ftype == PARSER_EQ || ftype == PARSER_NEQ) {
170+
return node->r;
171+
}
172+
}
173+
return nullptr;
174+
}
175+
176+
bool parser_is_comparison (struct parser_node* node)
177+
{
178+
if (node && node->type == PARSER_F2) {
179+
auto ftype = ((struct parser_f2*)node)->ftype;
180+
return (ftype == PARSER_LT || ftype == PARSER_GT ||
181+
ftype == PARSER_LEQ || ftype == PARSER_GEQ ||
182+
ftype == PARSER_EQ || ftype == PARSER_NEQ ||
183+
(ftype == PARSER_CMP_CHAIN && parser_is_comparison(node->r)));
184+
} else {
185+
return false;
186+
}
187+
}
188+
}
189+
190+
struct parser_node* parser_newcmpchain (struct parser_node* nl, enum parser_f2_t cmp,
191+
struct parser_node* nr)
192+
{
193+
/* If left side is already a comparison, this extends the chain */
194+
if (amrexpr::parser_is_comparison(nl)) {
195+
return amrexpr::parser_newf2(amrexpr::PARSER_CMP_CHAIN, nl,
196+
amrexpr::parser_newf2(cmp,
197+
amrexpr::parser_get_rightmost_operand(nl),
198+
nr));
199+
} else {
200+
return amrexpr::parser_newf2(cmp, nl, nr); // Initial comparison
201+
}
202+
}
203+
160204
/*******************************************************************/
161205

162206
struct amrexpr_parser*
@@ -1188,6 +1232,9 @@ parser_ast_optimize (struct parser_node*& node, std::map<std::string,double>& lo
11881232
case PARSER_F2:
11891233
parser_ast_optimize(((struct parser_f2*)node)->l, local_consts);
11901234
parser_ast_optimize(((struct parser_f2*)node)->r, local_consts);
1235+
if (((struct parser_f2*)node)->ftype == PARSER_CMP_CHAIN) {
1236+
((struct parser_f2*)node)->ftype = PARSER_AND;
1237+
}
11911238
if (((struct parser_f2*)node)->l->type == PARSER_NUMBER &&
11921239
((struct parser_f2*)node)->r->type == PARSER_NUMBER)
11931240
{
@@ -1197,6 +1244,54 @@ parser_ast_optimize (struct parser_node*& node, std::map<std::string,double>& lo
11971244
((struct parser_number*)(((struct parser_f2*)node)->r))->value);
11981245
parser_set_number(node, v);
11991246
}
1247+
else if (((struct parser_f2*)node)->ftype == PARSER_AND &&
1248+
((struct parser_f2*)node)->r->type == PARSER_NUMBER &&
1249+
parser_get_number(((struct parser_f2*)node)->r) == 0.0)
1250+
{ // ? and false => false
1251+
parser_set_number(node, 0.0);
1252+
}
1253+
else if (((struct parser_f2*)node)->ftype == PARSER_AND &&
1254+
((struct parser_f2*)node)->r->type == PARSER_NUMBER &&
1255+
parser_get_number(((struct parser_f2*)node)->r) != 0.0)
1256+
{ // ? and true => ?
1257+
std::memcpy(node, node->l, sizeof(struct parser_node));
1258+
}
1259+
else if (((struct parser_f2*)node)->ftype == PARSER_AND &&
1260+
((struct parser_f2*)node)->l->type == PARSER_NUMBER &&
1261+
parser_get_number(((struct parser_f2*)node)->l) == 0.0)
1262+
{ // false and ? => false
1263+
parser_set_number(node, 0.0);
1264+
}
1265+
else if (((struct parser_f2*)node)->ftype == PARSER_AND &&
1266+
((struct parser_f2*)node)->l->type == PARSER_NUMBER &&
1267+
parser_get_number(((struct parser_f2*)node)->l) != 0.0)
1268+
{ // true and ? => ?
1269+
std::memcpy(node, node->r, sizeof(struct parser_node));
1270+
}
1271+
else if (((struct parser_f2*)node)->ftype == PARSER_OR &&
1272+
((struct parser_f2*)node)->r->type == PARSER_NUMBER &&
1273+
parser_get_number(((struct parser_f2*)node)->r) != 0.0)
1274+
{ // ? or true => true
1275+
parser_set_number(node, 1.0);
1276+
}
1277+
else if (((struct parser_f2*)node)->ftype == PARSER_OR &&
1278+
((struct parser_f2*)node)->r->type == PARSER_NUMBER &&
1279+
parser_get_number(((struct parser_f2*)node)->r) == 0.0)
1280+
{ // ? or false => ?
1281+
std::memcpy(node, node->l, sizeof(struct parser_node));
1282+
}
1283+
else if (((struct parser_f2*)node)->ftype == PARSER_OR &&
1284+
((struct parser_f2*)node)->l->type == PARSER_NUMBER &&
1285+
parser_get_number(((struct parser_f2*)node)->l) != 0.0)
1286+
{ // true or ? => true
1287+
parser_set_number(node, 1.0);
1288+
}
1289+
else if (((struct parser_f2*)node)->ftype == PARSER_OR &&
1290+
((struct parser_f2*)node)->l->type == PARSER_NUMBER &&
1291+
parser_get_number(((struct parser_f2*)node)->l) == 0.0)
1292+
{ // false or ? => ?
1293+
std::memcpy(node, node->r, sizeof(struct parser_node));
1294+
}
12001295
else if (((struct parser_f2*)node)->ftype == PARSER_POW &&
12011296
((struct parser_f2*)node)->r->type == PARSER_NUMBER &&
12021297
parser_get_number(((struct parser_f2*)node)->r) == 0.0)

0 commit comments

Comments
 (0)