From eead97be3c784b124191b322eeec768d5fd41a62 Mon Sep 17 00:00:00 2001 From: Josh Rosen Date: Thu, 21 Nov 2024 23:46:02 -0800 Subject: [PATCH 1/4] Specialize StrReplace --- sjsonnet/src/sjsonnet/Std.scala | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sjsonnet/src/sjsonnet/Std.scala b/sjsonnet/src/sjsonnet/Std.scala index 3b90135b..f87a8041 100644 --- a/sjsonnet/src/sjsonnet/Std.scala +++ b/sjsonnet/src/sjsonnet/Std.scala @@ -440,6 +440,24 @@ class Std { private object StrReplace extends Val.Builtin3("strReplace", "str", "from", "to") { def evalRhs(str: Val, from: Val, to: Val, ev: EvalScope, pos: Position): Val = Val.Str(pos, str.asString.replace(from.asString, to.asString)) + + override def specialize(args: Array[Expr]): (Val.Builtin, Array[Expr]) = args match { + case Array(str, from: Val.Str, to: Val.Str) => + try { + val pattern = Pattern.compile(from.value, Pattern.LITERAL) + val quotedTo = java.util.regex.Matcher.quoteReplacement(to.value) + (new SpecStringReplace(pattern, quotedTo), Array(str)) + } catch { + case _: Exception => null + } + case _ => null + } + + private class SpecStringReplace(from: Pattern, quotedTo: String) extends Val.Builtin1("str") { + override def evalRhs(arg1: Val, ev: EvalScope, pos: Position): Val = { + Val.Str(pos, from.matcher(arg1.asString).replaceAll(quotedTo)) + } + } } private object StrReplaceAll extends Val.Builtin3("strReplaceAll", "str", "from", "to") { From 74b2f6d686d11ccfa6d7a4f0b2d4487623494972 Mon Sep 17 00:00:00 2001 From: Josh Rosen Date: Wed, 11 Dec 2024 23:39:16 -0800 Subject: [PATCH 2/4] Specialize strip functions --- sjsonnet/src/sjsonnet/Std.scala | 91 +++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 11 deletions(-) diff --git a/sjsonnet/src/sjsonnet/Std.scala b/sjsonnet/src/sjsonnet/Std.scala index f87a8041..7d5dceed 100644 --- a/sjsonnet/src/sjsonnet/Std.scala +++ b/sjsonnet/src/sjsonnet/Std.scala @@ -453,7 +453,7 @@ class Std { case _ => null } - private class SpecStringReplace(from: Pattern, quotedTo: String) extends Val.Builtin1("str") { + private class SpecStringReplace(from: Pattern, quotedTo: String) extends Val.Builtin1("strReplace", "str") { override def evalRhs(arg1: Val, ev: EvalScope, pos: Position): Val = { Val.Str(pos, from.matcher(arg1.asString).replaceAll(quotedTo)) } @@ -474,6 +474,82 @@ class Std { } } + private object StripUtils { + private def getLeadingPattern(chars: String): Pattern = + Pattern.compile("^[" + Regex.quote(chars) + "]+") + + private def getTrailingPattern(chars: String): Pattern = + Pattern.compile("[" + Regex.quote(chars) + "]+$") + + def unspecializedStrip(str: String, chars: String, left: Boolean, right: Boolean): String = { + var s = str + if (right) s = getTrailingPattern(chars).matcher(s).replaceAll("") + if (left) s = getLeadingPattern(chars).matcher(s).replaceAll("") + s + } + + private class SpecStrip( + chars: String, + left: Boolean, + right: Boolean, + functionName: String + ) extends Val.Builtin1(functionName, "str") { + private[this] val leftPattern = getLeadingPattern(chars) + private[this] val rightPattern = getTrailingPattern(chars) + + def evalRhs(str: Val, ev: EvalScope, pos: Position): Val = { + var s = str.asString + if (right) s = rightPattern.matcher(s).replaceAll("") + if (left) s = leftPattern.matcher(s).replaceAll("") + Val.Str(pos, s) + } + } + + def trySpecialize(str: Expr, chars: Val.Str, left: Boolean, right: Boolean, name: String): (Val.Builtin, Array[Expr]) = { + try { + (new SpecStrip(chars.value, left, right, name), Array(str)) + } catch { + case _: Exception => null + } + } + } + + object StripChars extends Val.Builtin2("stripChars", "str", "chars") { + def evalRhs(str: Val, chars: Val, ev: EvalScope, pos: Position): Val = { + Val.Str(pos, StripUtils.unspecializedStrip(str.asString, chars.asString, left = true, right = true)) + } + + override def specialize(args: Array[Expr]): (Val.Builtin, Array[Expr]) = args match { + case Array(str, chars: Val.Str) => + StripUtils.trySpecialize(str, chars, left = true, right = true, functionName) + case _ => null + } + } + + object LStripChars extends Val.Builtin2("lstripChars", "str", "chars") { + def evalRhs(str: Val, chars: Val, ev: EvalScope, pos: Position): Val = { + Val.Str(pos, StripUtils.unspecializedStrip(str.asString, chars.asString, left = true, right = false)) + } + + override def specialize(args: Array[Expr]): (Val.Builtin, Array[Expr]) = args match { + case Array(str, chars: Val.Str) => + StripUtils.trySpecialize(str, chars, left = true, right = false, functionName) + case _ => null + } + } + + object RStripChars extends Val.Builtin2("rstripChars", "str", "chars") { + def evalRhs(str: Val, chars: Val, ev: EvalScope, pos: Position): Val = { + Val.Str(pos, StripUtils.unspecializedStrip(str.asString, chars.asString, left = false, right = true)) + } + + override def specialize(args: Array[Expr]): (Val.Builtin, Array[Expr]) = args match { + case Array(str, chars: Val.Str) => + StripUtils.trySpecialize(str, chars, left = false, right = true, functionName) + case _ => null + } + } + private object Join extends Val.Builtin2("join", "sep", "arr") { def evalRhs(sep: Val, _arr: Val, ev: EvalScope, pos: Position): Val = { val arr = implicitly[ReadWriter[Val.Arr]].apply(_arr) @@ -1043,16 +1119,9 @@ class Std { builtin(Char_), builtin(StrReplace), builtin(StrReplaceAll), - - builtin("rstripChars", "str", "chars"){ (pos, ev, str: String, chars: String) => - str.replaceAll("[" + Regex.quote(chars) + "]+$", "") - }, - builtin("lstripChars", "str", "chars"){ (pos, ev, str: String, chars: String) => - str.replaceAll("^[" + Regex.quote(chars) + "]+", "") - }, - builtin("stripChars", "str", "chars"){ (pos, ev, str: String, chars: String) => - str.replaceAll("[" + Regex.quote(chars) + "]+$", "").replaceAll("^[" + Regex.quote(chars) + "]+", "") - }, + builtin(RStripChars), + builtin(LStripChars), + builtin(StripChars), builtin(Join), builtin(Member), From 27770b62c5205800cdfe4f98151a89f7826667a1 Mon Sep 17 00:00:00 2001 From: Josh Rosen Date: Wed, 11 Dec 2024 23:44:26 -0800 Subject: [PATCH 3/4] Renaming --- sjsonnet/src/sjsonnet/Std.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sjsonnet/src/sjsonnet/Std.scala b/sjsonnet/src/sjsonnet/Std.scala index 7d5dceed..bb469b27 100644 --- a/sjsonnet/src/sjsonnet/Std.scala +++ b/sjsonnet/src/sjsonnet/Std.scala @@ -446,14 +446,14 @@ class Std { try { val pattern = Pattern.compile(from.value, Pattern.LITERAL) val quotedTo = java.util.regex.Matcher.quoteReplacement(to.value) - (new SpecStringReplace(pattern, quotedTo), Array(str)) + (new SpecStrReplace(pattern, quotedTo), Array(str)) } catch { case _: Exception => null } case _ => null } - private class SpecStringReplace(from: Pattern, quotedTo: String) extends Val.Builtin1("strReplace", "str") { + private class SpecStrReplace(from: Pattern, quotedTo: String) extends Val.Builtin1("strReplace", "str") { override def evalRhs(arg1: Val, ev: EvalScope, pos: Position): Val = { Val.Str(pos, from.matcher(arg1.asString).replaceAll(quotedTo)) } From 98433eb727c5ce565fb45ef859a8f93bedd03d4c Mon Sep 17 00:00:00 2001 From: Josh Rosen Date: Thu, 12 Dec 2024 00:36:01 -0800 Subject: [PATCH 4/4] Undo string replacement spec: doesn't help on JDK 11+ --- sjsonnet/src/sjsonnet/Std.scala | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/sjsonnet/src/sjsonnet/Std.scala b/sjsonnet/src/sjsonnet/Std.scala index bb469b27..a76726e3 100644 --- a/sjsonnet/src/sjsonnet/Std.scala +++ b/sjsonnet/src/sjsonnet/Std.scala @@ -440,24 +440,6 @@ class Std { private object StrReplace extends Val.Builtin3("strReplace", "str", "from", "to") { def evalRhs(str: Val, from: Val, to: Val, ev: EvalScope, pos: Position): Val = Val.Str(pos, str.asString.replace(from.asString, to.asString)) - - override def specialize(args: Array[Expr]): (Val.Builtin, Array[Expr]) = args match { - case Array(str, from: Val.Str, to: Val.Str) => - try { - val pattern = Pattern.compile(from.value, Pattern.LITERAL) - val quotedTo = java.util.regex.Matcher.quoteReplacement(to.value) - (new SpecStrReplace(pattern, quotedTo), Array(str)) - } catch { - case _: Exception => null - } - case _ => null - } - - private class SpecStrReplace(from: Pattern, quotedTo: String) extends Val.Builtin1("strReplace", "str") { - override def evalRhs(arg1: Val, ev: EvalScope, pos: Position): Val = { - Val.Str(pos, from.matcher(arg1.asString).replaceAll(quotedTo)) - } - } } private object StrReplaceAll extends Val.Builtin3("strReplaceAll", "str", "from", "to") {