From 82a6f976f0a60fd13d088d570f3bc8e2fc40967c Mon Sep 17 00:00:00 2001 From: Rohit Agrawal Date: Fri, 18 Aug 2023 12:29:45 -0700 Subject: [PATCH] *New* Add parseYaml() Function (#142) ## Description This PR adds a new `parseYaml()` function to **sjsonnet**. [JSONNET Reference Doc](https://jsonnet.org/ref/stdlib.html#:~:text=yields%20%7B%20%22foo%22%3A%20%22bar%22%20%7D.-,std.parseYaml(str),-Available%20since%20version) --- **Signed-off-by:** Rohit Agrawal Signed-off-by: Rohit Agrawal --- build.sbt | 2 ++ build.sc | 4 +++- sjsonnet/src-js/sjsonnet/Platform.scala | 3 +++ sjsonnet/src-jvm/sjsonnet/Platform.scala | 8 ++++++++ sjsonnet/src-native/sjsonnet/Platform.scala | 3 +++ sjsonnet/src/sjsonnet/Std.scala | 13 +++++++++++-- .../test/src-jvm/sjsonnet/ParseYamlTests.scala | 15 +++++++++++++++ 7 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 sjsonnet/test/src-jvm/sjsonnet/ParseYamlTests.scala diff --git a/build.sbt b/build.sbt index 18e210e6..ab47d6af 100644 --- a/build.sbt +++ b/build.sbt @@ -16,8 +16,10 @@ lazy val main = (project in file("sjsonnet")) "com.lihaoyi" %% "scalatags" % "0.9.3", "com.lihaoyi" %% "os-lib" % "0.7.2", "com.lihaoyi" %% "mainargs" % "0.2.0", + "org.json" % "json" % "20211205", "org.scala-lang.modules" %% "scala-collection-compat" % "2.4.0", "org.tukaani" % "xz" % "1.8", + "org.yaml" % "snakeyaml" % "1.30", ), libraryDependencies ++= Seq( "com.lihaoyi" %% "utest" % "0.7.7", diff --git a/build.sc b/build.sc index 285ab643..73613bb0 100644 --- a/build.sc +++ b/build.sc @@ -105,7 +105,9 @@ class SjsonnetModule(val crossScalaVersion: String) extends Module { millSourcePath / "src-jvm-js" ) def ivyDeps = super.ivyDeps() ++ Agg( - ivy"org.tukaani:xz::1.8" + ivy"org.json:json:20211205", + ivy"org.tukaani:xz::1.8", + ivy"org.yaml:snakeyaml::1.30" ) def scalacOptions = Seq("-opt:l:inline", "-opt-inline-from:sjsonnet.**") //def compileIvyDeps = Agg( ivy"com.lihaoyi::acyclic:0.2.0") diff --git a/sjsonnet/src-js/sjsonnet/Platform.scala b/sjsonnet/src-js/sjsonnet/Platform.scala index 72165b28..01b47a32 100644 --- a/sjsonnet/src-js/sjsonnet/Platform.scala +++ b/sjsonnet/src-js/sjsonnet/Platform.scala @@ -12,6 +12,9 @@ object Platform { def xzString(s: String): String = { throw new Exception("XZ not implemented in Scala.js") } + def yamlToJson(s: String): String = { + throw new Exception("parseYaml() not implemented in Scala.js") + } def md5(s: String): String = { throw new Exception("MD5 not implemented in Scala.js") } diff --git a/sjsonnet/src-jvm/sjsonnet/Platform.scala b/sjsonnet/src-jvm/sjsonnet/Platform.scala index e8fbe16b..5537ba20 100644 --- a/sjsonnet/src-jvm/sjsonnet/Platform.scala +++ b/sjsonnet/src-jvm/sjsonnet/Platform.scala @@ -1,10 +1,14 @@ package sjsonnet +import org.json.JSONObject + import java.io.ByteArrayOutputStream import java.util.Base64 import java.util.zip.GZIPOutputStream import org.tukaani.xz.LZMA2Options import org.tukaani.xz.XZOutputStream +import org.yaml.snakeyaml.Yaml +import org.yaml.snakeyaml.constructor.Constructor object Platform { def gzipBytes(b: Array[Byte]): String = { @@ -31,6 +35,10 @@ object Platform { def xzString(s: String): String = { xzBytes(s.getBytes()) } + def yamlToJson(yamlString: String): String = { + val yaml: java.util.LinkedHashMap[String, Object] = new Yaml(new Constructor(classOf[java.util.LinkedHashMap[String, Object]])).load(yamlString) + new JSONObject(yaml).toString() + } def md5(s: String): String = { java.security.MessageDigest.getInstance("MD5") .digest(s.getBytes("UTF-8")) diff --git a/sjsonnet/src-native/sjsonnet/Platform.scala b/sjsonnet/src-native/sjsonnet/Platform.scala index c14187b5..bbd06664 100644 --- a/sjsonnet/src-native/sjsonnet/Platform.scala +++ b/sjsonnet/src-native/sjsonnet/Platform.scala @@ -12,6 +12,9 @@ object Platform { def xzString(s: String): String = { throw new Exception("XZ not implemented in Scala Native") } + def yamlToJson(s: String): String = { + throw new Exception("parseYaml() not implemented in Scala Native") + } def md5(s: String): String = { throw new Exception("MD5 not implemented in Scala Native") } diff --git a/sjsonnet/src/sjsonnet/Std.scala b/sjsonnet/src/sjsonnet/Std.scala index b3838375..56889251 100644 --- a/sjsonnet/src/sjsonnet/Std.scala +++ b/sjsonnet/src/sjsonnet/Std.scala @@ -5,10 +5,8 @@ import java.nio.charset.StandardCharsets.UTF_8 import java.util.Base64 import java.util import java.util.regex.Pattern - import sjsonnet.Expr.Member.Visibility import sjsonnet.Expr.BinaryOp -import sjsonnet.ArrayOps._ import scala.collection.mutable import scala.util.matching.Regex @@ -611,6 +609,16 @@ class Std { ujson.StringParser.transform(str.asString, new ValVisitor(pos)) } + private object ParseYaml extends Val.Builtin1("str") { + def evalRhs(str: Val, ev: EvalScope, pos: Position): Val = { + try { + ujson.StringParser.transform(Platform.yamlToJson(str.asString), new ValVisitor(pos)) + } catch { + case _: Exception => null + } + } + } + private object Set_ extends Val.Builtin2("arr", "keyF", Array(null, Val.False(dummyPos))) { def evalRhs(arr: Val, keyF: Val, ev: EvalScope, pos: Position): Val = { uniqArr(pos, ev, sortArr(pos, ev, arr, keyF), keyF) @@ -1183,6 +1191,7 @@ class Std { "parseOctal" -> ParseOctal, "parseHex" -> ParseHex, "parseJson" -> ParseJson, + "parseYaml" -> ParseYaml, "md5" -> MD5, builtin("prune", "x"){ (pos, ev, s: Val) => def filter(x: Val) = x match{ diff --git a/sjsonnet/test/src-jvm/sjsonnet/ParseYamlTests.scala b/sjsonnet/test/src-jvm/sjsonnet/ParseYamlTests.scala new file mode 100644 index 00000000..d36a7e2a --- /dev/null +++ b/sjsonnet/test/src-jvm/sjsonnet/ParseYamlTests.scala @@ -0,0 +1,15 @@ +package sjsonnet + +import sjsonnet.TestUtils.eval +import utest._ + +object ParseYamlTests extends TestSuite { + def tests: Tests = Tests { + test { + eval("std.parseYaml('foo: bar')") ==> ujson.Value("""{"foo":"bar"}""") + } + test { + eval("std.parseYaml('')") ==> ujson.Value("""{}""") + } + } +}