[
https://issues.apache.org/jira/browse/JEXL-438?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17941233#comment-17941233
]
Henri Biestro edited comment on JEXL-438 at 4/5/25 12:24 PM:
-------------------------------------------------------------
The easiest part is configuring to avoid syntactic constructs that dont look
like expressions:
{code:java}
// no local, no lambda, no loops, no-side effects
final JexlFeatures f = new JexlFeatures()
.localVar(false)
.lambda(false)
.loops(false)
.sideEffect(false)
.sideEffectGlobal(false);
JexlBuilder builder = new JexlBuilder();
builder.features(f);
// more settings...
JexlEngine jexl = builder.create();
{code}
The fastest way without modifications to JEXL code is to transcode those
SQL-like expressions to JEXL, something like:
{code:java}
/** The set of characters that may be followed by a '='.*/
static final char[] EQ_FRIEND;
static {
char[] eq = {'!', ':', '<', '>', '^', '|', '&', '+', '-', '/', '*',
'~', '='};
Arrays.sort(eq);
EQ_FRIEND = eq;
}
/**
* Transcodes a SQL-inspired expression to a JEXL expression.
* @param expr the expression to transcode
* @return the resulting expression
*/
private static String transcodeSQLExpr(final CharSequence expr) {
final StringBuilder strb = new StringBuilder(expr.length());
final int end = expr.length();
char previous = 0;
for (int i = 0; i < end; ++i) {
char c = expr.charAt(i);
if (previous == '<') {
// previous char a '<' now followed by '>'
if (c == '>') {
// replace '<>' with '!='
strb.append("!=");
previous = c;
continue;
} else {
strb.append('<');
}
}
if (c != '<') {
if (c == '=') {
// replace '=' with '==' when it does not follow a 'friend'
if (Arrays.binarySearch(EQ_FRIEND, previous) >= 0) {
strb.append(c);
} else {
strb.append("==");
}
} else {
strb.append(c);
if (c == '"' || c == '\'') {
// read string, escape '\'
boolean escape = false;
for (i += 1; i < end; ++i) {
final char ec = expr.charAt(i);
strb.append(ec);
if (ec == '\\') {
escape = !escape;
} else if (escape) {
escape = false;
} else if (ec == c) {
break;
}
}
}
}
}
previous = c;
}
return strb.toString();
}
@Test
void testSQLTranspose() {
String[] e = { "a<>b", "a = 2", "a.b.c <> '1<>0'" };
String[] j = { "a!=b", "a == 2", "a.b.c != '1<>0'" };
for(int i = 0; i < e.length; ++i) {
String je = transcodeSQLExpr(e[i]);
Assertions.assertEquals(j[i], je);
}
}
@Test
void testSQLNoChange() {
String[] e = { "a <= 2", "a >= 2", "a := 2", "a + 3 << 4 > 5", };
for(int i = 0; i < e.length; ++i) {
String je = transcodeSQLExpr(e[i]);
Assertions.assertEquals(e[i], je);
}
}
{code}
This might be crude but solves the problem as stated.
was (Author: henrib):
The easiest part is configuring to avoid syntactic constructs that dont look
like expressions:
{code:java}
// Some comments here
// no local, no lambda, no loops, no-side effects
final JexlFeatures f = new JexlFeatures()
.localVar(false)
.lambda(false)
.loops(false)
.sideEffect(false)
.sideEffectGlobal(false);
JexlBuilder builder = new JexlBuilder();
builder.features(f);
// more settings...
JexlEngine jexl = builder.create();
{code}
The fastest way without modifications to JEXL code is to transcode those
SQL-like expressions to JEXL, something like:
{code:java}
/** The set of characters that may be followed by a '='.*/
static final char[] EQ_FRIEND;
static {
char[] eq = {'!', ':', '<', '>', '^', '|', '&', '+', '-', '/', '*',
'~', '='};
Arrays.sort(eq);
EQ_FRIEND = eq;
}
/**
* Transcodes a SQL-inspired expression to a JEXL expression.
* @param expr the expression to transcode
* @return the resulting expression
*/
private static String transcodeSQLExpr(final CharSequence expr) {
final StringBuilder strb = new StringBuilder(expr.length());
final int end = expr.length();
char previous = 0;
for (int i = 0; i < end; ++i) {
char c = expr.charAt(i);
if (previous == '<') {
// previous char a '<' now followed by '>'
if (c == '>') {
// replace '<>' with '!='
strb.append("!=");
previous = c;
continue;
} else {
strb.append('<');
}
}
if (c != '<') {
if (c == '=') {
// replace '=' with '==' when it does not follow a 'friend'
if (Arrays.binarySearch(EQ_FRIEND, previous) >= 0) {
strb.append(c);
} else {
strb.append("==");
}
} else {
strb.append(c);
if (c == '"' || c == '\'') {
// read string, escape '\'
boolean escape = false;
for (i += 1; i < end; ++i) {
final char ec = expr.charAt(i);
strb.append(ec);
if (ec == '\\') {
escape = !escape;
} else if (escape) {
escape = false;
} else if (ec == c) {
break;
}
}
}
}
}
previous = c;
}
return strb.toString();
}
@Test
void testSQLTranspose() {
String[] e = { "a<>b", "a = 2", "a.b.c <> '1<>0'" };
String[] j = { "a!=b", "a == 2", "a.b.c != '1<>0'" };
for(int i = 0; i < e.length; ++i) {
String je = transcodeSQLExpr(e[i]);
Assertions.assertEquals(j[i], je);
}
}
@Test
void testSQLNoChange() {
String[] e = { "a <= 2", "a >= 2", "a := 2", "a + 3 << 4 > 5", };
for(int i = 0; i < e.length; ++i) {
String je = transcodeSQLExpr(e[i]);
Assertions.assertEquals(e[i], je);
}
}
{code}
This might be crude but solves the problem as stated.
> Allow alternative syntax: sql/excel-like
> ----------------------------------------
>
> Key: JEXL-438
> URL: https://issues.apache.org/jira/browse/JEXL-438
> Project: Commons JEXL
> Issue Type: New Feature
> Reporter: Yair Lenga
> Priority: Major
>
> Im using jexl3 to allow customization by end users of workflow. The users
> love the flexibility, but are having repeated issues with the grammar.
> Specifically, the == operator, not having ‘<>’. I understand the parser
> cannot be configured at run time flag, as it is generated code by JavaScript,
> and changing = to mean equals will change parsing logic, operator precedence,
> etc.
> My ask: possible to allow for an alternate parser (parser-sql.jjt), which
> will be modified/extended version of the existing parser.jjt, and will
> provide more sql/excel operators. in particular, no assignment inside
> expressions, etc.
> Implementation can allow for a Boolean mode, 'Sqlish', same as 'antish'
> property. I'm not sure if possible to build a parser outside the jexl3 tree.
> If yes, will be nice to be able to pass a class that implement the same
> interface as the current parser.
> For my use case, I do not mind having all the jexl3 extension features, even
> the one not in the sql/excel grammar. I do not need the ability to have
> statements, scripts, etc.
> there are some other minor twists (concatenation, etc), but I believe those
> can be handled via jex3 operator customization. Some constructs (sql case)
> will require some additional work, but are not high priority for my use case.
>
--
This message was sent by Atlassian Jira
(v8.20.10#820010)