Skip to content

Commit

Permalink
Expression STRICT_ESCAPES mode
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez committed Sep 25, 2024
1 parent f3eefdd commit 0858c39
Show file tree
Hide file tree
Showing 2 changed files with 194 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ private static Node parseString(Itr itr, final boolean allowExpr, final boolean
}
// check to see if it's a dangling $
if (!itr.hasNext()) {
if (flags.contains(Flag.NO_$$)) {
continue;
}
if (!flags.contains(Flag.LENIENT_SYNTAX)) {
// TP 2
throw invalidExpressionSyntax(itr.getStr(), idx);
Expand All @@ -269,16 +272,16 @@ private static Node parseString(Itr itr, final boolean allowExpr, final boolean
start = itr.getNextIdx();
continue;
}
// enqueue what we have acquired so far
if (idx > start) {
// TP 4
list.add(new LiteralNode(itr.getStr(), start, idx));
}
// next char should be an expression starter of some sort
idx = itr.getNextIdx();
ch = itr.next();
switch (ch) {
case '{': {
// enqueue what we have acquired so far
if (idx > start) {
// TP 4
list.add(new LiteralNode(itr.getStr(), start, idx - 1));
}
// ${
boolean general = flags.contains(Flag.GENERAL_EXPANSION) && itr.hasNext() && itr.peekNext() == '{';
// consume double-{
Expand Down Expand Up @@ -407,18 +410,47 @@ private static Node parseString(Itr itr, final boolean allowExpr, final boolean
}
case '$': {
// $$
if (flags.contains(Flag.MINI_EXPRS)) {
// TP 13
list.add(new ExpressionNode(false, LiteralNode.DOLLAR, Node.NULL));
if (flags.contains(Flag.NO_$$)) {
if (itr.hasNext()) {
switch (itr.peekNext()) {
case '{':
list.add(new LiteralNode(itr.getStr(), start, idx));
break;
case '$':
list.add(new LiteralNode(itr.getStr(), start, idx));
itr.prev();
break;
default:
list.add(new LiteralNode(itr.getStr(), start, itr.getNextIdx()));
break;
}
} else {
list.add(new LiteralNode(itr.getStr(), start, itr.getNextIdx()));
}
} else {
// just resolve $$ to $
// TP 14
list.add(LiteralNode.DOLLAR);
// enqueue what we have acquired so far
if (idx > start) {
// TP 4
list.add(new LiteralNode(itr.getStr(), start, idx - 1));
}
if (flags.contains(Flag.MINI_EXPRS)) {
// TP 13
list.add(new ExpressionNode(false, LiteralNode.DOLLAR, Node.NULL));
} else {
// just resolve $$ to $
// TP 14
list.add(LiteralNode.DOLLAR);
}
}
start = itr.getNextIdx();
continue;
}
case '}': {
// enqueue what we have acquired so far
if (idx > start) {
// TP 4
list.add(new LiteralNode(itr.getStr(), start, idx - 1));
}
// $}
if (flags.contains(Flag.MINI_EXPRS)) {
// TP 15
Expand Down Expand Up @@ -452,6 +484,11 @@ private static Node parseString(Itr itr, final boolean allowExpr, final boolean
//throw Assert.unreachableCode();
}
case ':': {
// enqueue what we have acquired so far
if (idx > start) {
// TP 4
list.add(new LiteralNode(itr.getStr(), start, idx - 1));
}
// $:
if (flags.contains(Flag.MINI_EXPRS)) {
// $: is an expression
Expand Down Expand Up @@ -485,7 +522,22 @@ private static Node parseString(Itr itr, final boolean allowExpr, final boolean
}
//throw Assert.unreachableCode();
}
case '\\': {
if (flags.contains(Flag.NO_$$)) {
if (itr.hasNext() && itr.peekNext() == '$') {
list.add(new LiteralNode(itr.getStr(), start, idx));
start = itr.getNextIdx();
itr.next();
continue;
}
}
}
default: {
// enqueue what we have acquired so far
if (idx > start) {
// TP 4
list.add(new LiteralNode(itr.getStr(), start, idx - 1));
}
// $ followed by anything else
if (flags.contains(Flag.MINI_EXPRS)) {
// TP 25
Expand Down Expand Up @@ -551,7 +603,22 @@ private static Node parseString(Itr itr, final boolean allowExpr, final boolean
//throw Assert.unreachableCode();
}
case '\\': {
if (flags.contains(Flag.ESCAPES)) {
if (flags.contains(Flag.NO_$$)) {
int escape = itr.getNextIdx();
if (itr.hasNext() && itr.peekNext() == '$') {
itr.next();
if (itr.hasNext() && itr.peekNext() == '{') {
list.add(new LiteralNode(itr.getStr(), start, escape - 1));
list.add(new LiteralNode(itr.getStr(), escape, itr.getNextIdx()));
start = itr.getNextIdx();
} else if (itr.hasNext() && itr.peekNext() == '$') {
list.add(new LiteralNode(itr.getStr(), start, escape));
start = itr.getPrevIdx();
itr.prev();
}
}
continue;
} else if (flags.contains(Flag.ESCAPES)) {
if (idx > start) {
list.add(new LiteralNode(itr.getStr(), start, idx));
start = idx;
Expand Down Expand Up @@ -694,6 +761,11 @@ public enum Flag {
* character.
*/
ESCAPES,
/**
* Escaping <code>$</code> with <code>$$</code> or <code>/$</code> only applies when <code>{</code> follows
* the initial escaped <code>$</code>.
*/
NO_$$,
/**
* Treat expressions containing a double-colon delimiter as special, encoding the entire content into the key.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import static io.smallrye.common.expression.Expression.Flag.NO_SMART_BRACES;
import static org.junit.jupiter.api.Assertions.*;

import java.util.EnumSet;

import org.junit.jupiter.api.Test;

import io.smallrye.common.constraint.Assert;
Expand Down Expand Up @@ -693,4 +695,112 @@ void expressions() {
b.append(c.getExpandedDefault());
}));
}

@Test
void no$$() {
doubleDollarExpressions(EnumSet.of(NO_$$));
doubleDollarExpressions(EnumSet.of(NO_$$, MINI_EXPRS));

assertEquals("", Expression.compile("$a", NO_$$, MINI_EXPRS).evaluate((c, b) -> {
assertEquals("a", c.getKey());
}));
assertEquals("$$a", Expression.compile("$$a", NO_$$, MINI_EXPRS).evaluate((c, b) -> {
}));

doubleDollarExpressions(EnumSet.of(NO_$$, ESCAPES));
doubleDollarExpressions(EnumSet.of(NO_$$, ESCAPES, MINI_EXPRS));
}

private void doubleDollarExpressions(EnumSet<Expression.Flag> flags) {
assertEquals("$", Expression.compile("$", flags).evaluate((c, b) -> {
}));
assertEquals("$$", Expression.compile("$$", flags).evaluate((c, b) -> {
}));
assertEquals("\\$", Expression.compile("\\$", flags).evaluate((c, b) -> {
}));
assertEquals("\\$$", Expression.compile("\\$$", flags).evaluate((c, b) -> {
}));
assertEquals("$$foo", Expression.compile("$$foo", flags).evaluate((c, b) -> {
}));
assertEquals("foo$$", Expression.compile("foo$$", flags).evaluate((c, b) -> {
}));
assertEquals("$$foo", Expression.compile("$$foo", flags).evaluate((c, b) -> {
}));
assertEquals("foo$$bar", Expression.compile("foo$$bar", flags).evaluate((c, b) -> {
}));
assertEquals("${foo}", Expression.compile("$${foo}", flags).evaluate((c, b) -> {
}));
assertEquals("$${foo}", Expression.compile("$$${foo}", flags).evaluate((c, b) -> {
}));
assertEquals("$${foo}$", Expression.compile("$$${foo}$", flags).evaluate((c, b) -> {
}));
assertEquals("$${foo}$$", Expression.compile("$$${foo}$$", flags).evaluate((c, b) -> {
}));
assertEquals("foo${bar}", Expression.compile("foo$${bar}", flags).evaluate((c, b) -> {
}));
assertEquals("foo$${bar}", Expression.compile("foo$$${bar}", flags).evaluate((c, b) -> {
}));
assertEquals("foo$$$${bar}", Expression.compile("foo$$$$${bar}", flags).evaluate((c, b) -> {
}));
assertEquals("foo$$$${bar}$$$baz", Expression.compile("foo$$$$${bar}$$$baz", flags).evaluate((c, b) -> {
}));
assertEquals("foo$$$$", Expression.compile("foo$$$$", flags).evaluate((c, b) -> {
}));
assertEquals("${foo:bar}", Expression.compile("$${foo:bar}", flags).evaluate((c, b) -> {
}));
assertEquals("$${foo:bar}", Expression.compile("$$${foo:bar}", flags).evaluate((c, b) -> {
}));
assertEquals("${foo:}", Expression.compile("$${foo:${bar}}", flags).evaluate((c, b) -> {
}));
assertEquals("${foo:${bar}}", Expression.compile("$${foo:$${bar}}", flags).evaluate((c, b) -> {
}));

assertEquals("", Expression.compile("${foo}", flags).evaluate((c, b) -> {
assertEquals("foo", c.getKey());
}));
assertEquals("", Expression.compile("${foo}${bar}", flags).evaluate((c, b) -> {
if ("foo".equals(c.getKey()))
assertEquals("foo", c.getKey());
if ("bar".equals(c.getKey()))
assertEquals("bar", c.getKey());
}));
assertEquals("foobar", Expression.compile("foo${foo}${bar}bar", flags).evaluate((c, b) -> {
if ("foo".equals(c.getKey()))
assertEquals("foo", c.getKey());
if ("bar".equals(c.getKey()))
assertEquals("bar", c.getKey());
}));
assertEquals("foo${foo}bar", Expression.compile("foo$${foo}${bar}bar", flags).evaluate((c, b) -> {
if ("bar".equals(c.getKey()))
assertEquals("bar", c.getKey());
}));
assertEquals("foo${foo}bar", Expression.compile("foo$${foo${bar}}bar", flags).evaluate((c, b) -> {
if ("bar".equals(c.getKey()))
assertEquals("bar", c.getKey());
}));
assertEquals("", Expression.compile("${}", flags).evaluate((c, b) -> {
assertEquals("", c.getKey());
}));
assertEquals("", Expression.compile("${:}", flags).evaluate((c, b) -> {
assertEquals("", c.getKey());
}));

assertEquals("${foo}", Expression.compile("\\${foo}", flags).evaluate((c, b) -> {
}));
assertEquals("${foo}bar", Expression.compile("\\${foo}bar", flags).evaluate((c, b) -> {
}));
assertEquals("\\$\\{%s}", Expression.compile("\\$\\{%s}", flags).evaluate((c, b) -> {
}));
assertEquals("foo${bar}", Expression.compile("foo\\${bar}", flags).evaluate((c, b) -> {
}));
assertEquals("foo\\${bar}", Expression.compile("foo\\\\${bar}", flags).evaluate((c, b) -> {
}));

assertEquals("foo\\${bar}", Expression.compile("foo\\$${bar}", flags).evaluate((c, b) -> {
}));
assertEquals("foo$${bar}", Expression.compile("foo$\\${bar}", flags).evaluate((c, b) -> {
}));
assertEquals("foo$$\\{bar}", Expression.compile("foo$$\\{bar}", flags).evaluate((c, b) -> {
}));
}
}

0 comments on commit 0858c39

Please sign in to comment.