Skip to content

Commit 0a11dc1

Browse files
authored
Merge pull request #930 from camunda/879-escapa-chars
fix: Handle escape and regex characters
2 parents f170c0a + d266e80 commit 0a11dc1

File tree

2 files changed

+58
-43
lines changed

2 files changed

+58
-43
lines changed

src/main/scala/org/camunda/feel/impl/parser/FeelParser.scala

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -694,22 +694,16 @@ object FeelParser {
694694
}.getOrElse(ConstNull)
695695
}
696696

697-
// replace escaped character with the provided replacement
698697
private def translateEscapes(input: String): String = {
699-
val escapeMap = Map(
700-
"\\b" -> "\b",
701-
"\\t" -> "\t",
702-
"\\n" -> "\n",
703-
"\\f" -> "\f",
704-
"\\r" -> "\r",
705-
"\\\"" -> "\"",
706-
"\\'" -> "'",
707-
"\\s" -> " "
708-
// Add more escape sequences as needed
709-
)
710-
711-
escapeMap.foldLeft(input) { case (result, (escape, replacement)) =>
712-
result.replace(escape, replacement)
713-
}
698+
// replace all escape sequences
699+
input
700+
.replaceAll("(?<!\\\\)\\\\n", "\n") // new line
701+
.replaceAll("(?<!\\\\)\\\\r", "\r") // carriage return
702+
.replaceAll("(?<!\\\\)\\\\t", "\t") // tab
703+
.replaceAll("(?<!\\\\)\\\\b", "\b") // backspace
704+
.replaceAll("(?<!\\\\)\\\\f", "\f") // form feed
705+
.replaceAll("(?<!\\\\)\\\\'", "'") // single quote
706+
.replaceAll("(?<!\\\\)\\\\\"", "\"") // double quote
707+
.replaceAll("\\\\\\\\", "\\\\") // backslash (for regex characters)
714708
}
715709
}

src/test/scala/org/camunda/feel/impl/interpreter/InterpreterStringExpressionTest.scala

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import org.camunda.feel.impl.{EvaluationResultMatchers, FeelEngineTest, FeelInte
2020
import org.camunda.feel.syntaxtree._
2121
import org.scalatest.matchers.should.Matchers
2222
import org.scalatest.flatspec.AnyFlatSpec
23+
import org.scalatest.prop.TableDrivenPropertyChecks
2324

2425
import scala.collection.immutable.Map
2526

@@ -30,7 +31,8 @@ class InterpreterStringExpressionTest
3031
extends AnyFlatSpec
3132
with Matchers
3233
with FeelEngineTest
33-
with EvaluationResultMatchers {
34+
with EvaluationResultMatchers
35+
with TableDrivenPropertyChecks {
3436

3537
"A string" should "concatenates to another String" in {
3638

@@ -80,40 +82,59 @@ class InterpreterStringExpressionTest
8082
evaluateExpression(""" "a" != null """) should returnResult(true)
8183
}
8284

83-
it should "return not escaped characters" in {
84-
85-
evaluateExpression(""" "Hello\nWorld" """) should returnResult("Hello\nWorld")
86-
evaluateExpression(" x ", Map("x" -> "Hello\nWorld")) should returnResult("Hello\nWorld")
85+
private val escapeSequences = Table(
86+
("Character", "Expected", "Display name"),
87+
('\n', '\n', "new line"),
88+
('\r', '\r', "carriage return"),
89+
('\t', '\t', "tab"),
90+
('\b', '\b', "backspace"),
91+
('\f', '\f', "form feed"),
92+
('\'', '\'', "single quote"),
93+
("\\\"", '"', "double quote"),
94+
("\\\\", '\\', "backslash")
95+
)
8796

88-
evaluateExpression(""" "Hello\rWorld" """) should returnResult("Hello\rWorld")
89-
evaluateExpression(" x ", Map("x" -> "Hello\rWorld")) should returnResult("Hello\rWorld")
97+
it should "contains an escape sequence" in {
98+
forEvery(escapeSequences) { (character, expected, _) =>
99+
val expectedString = s"a $expected b"
90100

91-
evaluateExpression(""" "Hello\'World" """) should returnResult("Hello\'World")
92-
evaluateExpression(" x ", Map("x" -> "Hello\'World")) should returnResult("Hello\'World")
101+
evaluateExpression(s" \"a $character b\" ") should returnResult(expectedString)
102+
evaluateExpression("char", Map("char" -> expectedString)) should returnResult(expectedString)
103+
}
104+
}
93105

94-
evaluateExpression(""" "Hello\tWorld" """) should returnResult("Hello\tWorld")
95-
evaluateExpression(" x ", Map("x" -> "Hello\tWorld")) should returnResult("Hello\tWorld")
106+
private val unicodeCharacters = Table(
107+
("Character", "Display name"),
108+
('\u269D', "\\u269D"),
109+
("\\U101EF", "\\U101EF")
110+
)
96111

97-
evaluateExpression(""" "Hello\"World" """) should returnResult("Hello\"World")
98-
evaluateExpression(" x ", Map("x" -> "Hello\"World")) should returnResult("Hello\"World")
112+
it should "contains unicode characters" in {
113+
forEvery(unicodeCharacters) { (character, _) =>
114+
evaluateExpression(s" \"a $character b\" ") should returnResult(s"a $character b")
115+
}
99116
}
100117

101-
List(
102-
" \' ",
103-
" \\ ",
104-
" \n ",
105-
" \r ",
106-
" \t ",
107-
""" \u269D """,
108-
""" \U101EF """
118+
private val regexCharacters = Table(
119+
("Character", "Display name"),
120+
("\\s", "\\s"),
121+
("\\S", "\\S"),
122+
("\\d", "\\d"),
123+
("\\w", "\\w"),
124+
("\\R", "\\R"),
125+
("\\h", "\\h"),
126+
("\\v", "\\v"),
127+
("\\\n", "\\n"),
128+
("\\\r", "\\r")
109129
)
110-
.foreach { notEscapeChar =>
111-
it should s"contains a not escape sequence ($notEscapeChar)" in {
112130

113-
evaluateExpression(s""" "a $notEscapeChar b" """) should returnResult(
114-
s"""a $notEscapeChar b"""
115-
)
116-
}
131+
it should "contains a regex character" in {
132+
forEvery(regexCharacters) { (character, _) =>
133+
val expectedString = s"a $character b"
134+
135+
evaluateExpression(s" \"a $character b\" ") should returnResult(expectedString)
136+
evaluateExpression("char", Map("char" -> expectedString)) should returnResult(expectedString)
117137
}
138+
}
118139

119140
}

0 commit comments

Comments
 (0)