Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bench/src/main/scala/sjsonnet/MainBenchmark.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ object MainBenchmark {
val parseCache = new DefaultParseCache
val interp = new Interpreter(
Map.empty[String, String],
Map.empty[String, ujson.Value],
Map.empty[String, String],
OsPath(wd),
importer = SjsonnetMain.resolveImport(config.jpaths.map(os.Path(_, wd)).map(OsPath(_)), None),
parseCache = parseCache
Expand Down
2 changes: 1 addition & 1 deletion bench/src/main/scala/sjsonnet/MaterializerBenchmark.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class MaterializerBenchmark {
var currentPos: Position = null
this.interp = new Interpreter(
Map.empty[String, String],
Map.empty[String, ujson.Value],
Map.empty[String, String],
OsPath(wd),
importer = SjsonnetMain.resolveImport(config.jpaths.map(os.Path(_, wd)).map(OsPath(_)), None),
parseCache = new DefaultParseCache
Expand Down
2 changes: 1 addition & 1 deletion bench/src/main/scala/sjsonnet/RunProfiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ object RunProfiler extends App {
val parseCache = new DefaultParseCache
val interp = new Interpreter(
Map.empty[String, String],
Map.empty[String, ujson.Value],
Map.empty[String, String],
OsPath(wd),
importer = SjsonnetMain.resolveImport(config.jpaths.map(os.Path(_, wd)).map(OsPath(_)), None),
parseCache = parseCache
Expand Down
2 changes: 1 addition & 1 deletion sjsonnet/src-js/sjsonnet/SjsonnetMain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ object SjsonnetMain {
preserveOrder: Boolean = false): js.Any = {
val interp = new Interpreter(
ujson.WebJson.transform(extVars, ujson.Value).obj.toMap.map{case (k, ujson.Str(v)) => (k, v)},
ujson.WebJson.transform(tlaVars, ujson.Value).obj.toMap,
ujson.WebJson.transform(tlaVars, ujson.Value).obj.toMap.map{case (k, ujson.Str(v)) => (k, v)},
JsVirtualPath(wd0),
new Importer {
def resolve(docBase: Path, importName: String): Option[Path] =
Expand Down
67 changes: 32 additions & 35 deletions sjsonnet/src-jvm-native/sjsonnet/SjsonnetMain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,27 @@ object SjsonnetMain {

def isScalar(v: ujson.Value) = !v.isInstanceOf[ujson.Arr] && !v.isInstanceOf[ujson.Obj]

def parseBindings(strs: Seq[String],
strFiles: Seq[String],
codes: Seq[String],
codeFiles: Seq[String],
wd: os.Path) = {

def split(s: String) = s.split("=", 2) match{
case Array(x) => (x, System.getenv(x))
case Array(x, v) => (x, v)
}

def splitMap(s: Seq[String], f: String => String) = s.map(split).map{case (x, v) => (x, f(v))}
def readPath(v: String) = os.read(os.Path(v, wd))

Map() ++
splitMap(strs, v => ujson.write(v)) ++
splitMap(strFiles, v => ujson.write(readPath(v))) ++
splitMap(codes, identity) ++
splitMap(codeFiles, readPath)
}

/**
* @return Right(str) if there's some string that needs to be printed to stdout or
* --output-file, Left(err) if there is an error to be reported
Expand All @@ -159,45 +180,21 @@ object SjsonnetMain {
val (jsonnetCode, path) =
if (config.exec.value) (file, wd / "<exec>") else (os.read(os.Path(file)), os.Path(file))

var varBinding = Map.empty[String, String]
config.extStr.map(_.split("=", 2)).foreach{
case Array(x) => varBinding = varBinding ++ Seq(x -> ujson.write(System.getenv(x)))
case Array(x, v) => varBinding = varBinding ++ Seq(x -> ujson.write(v))
}
config.extStrFile.map(_.split("=", 2)).foreach {
case Array(x, v) =>
varBinding = varBinding ++ Seq(x -> ujson.write(os.read(os.Path(v, wd))))
}
config.extCode.map(_.split("=", 2)).foreach {
case Array(x) => varBinding = varBinding ++ Seq(x -> System.getenv(x))
case Array(x, v) => varBinding = varBinding ++ Seq(x -> v)
}
config.extCodeFile.map(_.split("=", 2)).foreach {
case Array(x, v) =>
varBinding = varBinding ++ Seq(x -> os.read(os.Path(v, wd)))
}
val extBinding = parseBindings(
config.extStr, config.extStrFile,
config.extCode, config.extCodeFile,
wd
)

var tlaBinding = Map.empty[String, ujson.Value]
val tlaBinding = parseBindings(
config.tlaStr, config.tlaStrFile,
config.tlaCode, config.tlaCodeFile,
wd
)

config.tlaStr.map(_.split("=", 2)).foreach{
case Array(x) => tlaBinding = tlaBinding ++ Seq(x -> ujson.Str(System.getenv(x)))
case Array(x, v) => tlaBinding = tlaBinding ++ Seq(x -> ujson.Str(v))
}
config.tlaStrFile.map(_.split("=", 2)).foreach {
case Array(x, v) =>
tlaBinding = tlaBinding ++ Seq(x -> ujson.Str(os.read(os.Path(v, wd))))
}
config.tlaCode.map(_.split("=", 2)).foreach {
case Array(x) => tlaBinding = tlaBinding ++ Seq(x -> ujson.read(System.getenv(x)))
case Array(x, v) => tlaBinding = tlaBinding ++ Seq(x -> ujson.read(v))
}
config.tlaCodeFile.map(_.split("=", 2)).foreach {
case Array(x, v) =>
tlaBinding = tlaBinding ++ Seq(x -> ujson.read(os.read(os.Path(v, wd))))
}
var currentPos: Position = null
val interp = new Interpreter(
varBinding,
extBinding,
tlaBinding,
OsPath(wd),
importer = importer match{
Expand Down
23 changes: 14 additions & 9 deletions sjsonnet/src/sjsonnet/Interpreter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ import java.io.{PrintWriter, StringWriter}

import sjsonnet.Expr.Params

import scala.collection.mutable
import scala.util.control.NonFatal

/**
* Wraps all the machinery of evaluating Jsonnet source code, from parsing to
* evaluation to materialization, into a convenient wrapper class.
*/
class Interpreter(extVars: Map[String, String],
tlaVars: Map[String, ujson.Value],
tlaVars: Map[String, String],
wd: Path,
importer: Importer,
val parseCache: ParseCache,
Expand All @@ -33,12 +32,15 @@ class Interpreter(extVars: Map[String, String],
settings: Settings, warn: Error => Unit): Evaluator =
new Evaluator(resolver, extVars, wd, settings, warn)


def parseVar(k: String, v: String) = {
resolver.parse(wd / s"<$k>", v)(evaluator).fold(throw _, _._1)
}

lazy val evaluator: Evaluator = createEvaluator(
resolver,
// parse extVars lazily, because they can refer to each other and be recursive
extVars
.mapValues{ v => resolver.parse(wd / s"<ext-var $v>", v)(evaluator).fold(throw _, _._1) }
.lift,
k => extVars.get(k).map(v => parseVar(s"ext-var $k", v)),
wd,
settings,
warn
Expand Down Expand Up @@ -73,7 +75,7 @@ class Interpreter(extVars: Map[String, String],
}
}

def evaluate[T](txt: String, path: Path): Either[Error, Val] = {
def evaluate(txt: String, path: Path): Either[Error, Val] = {
resolver.cache(path) = txt
for{
res <- resolver.parse(path, txt)(evaluator)
Expand All @@ -84,15 +86,18 @@ class Interpreter(extVars: Map[String, String],
val defaults2 = f.params.defaultExprs.clone()
var i = 0
while(i < defaults2.length) {
tlaVars.get(f.params.names(i)) match {
case Some(v) => defaults2(i) = Materializer.toExpr(v)(evaluator)
val k = f.params.names(i)
tlaVars.get(k) match {
case Some(v) => defaults2(i) = parseVar(s"tla-var $k", v)
case None =>
}
i += 1
}
new Val.Func(f.pos, f.defSiteValScope, Params(f.params.names, defaults2)) {
def evalRhs(vs: ValScope, es: EvalScope, fs: FileScope, pos: Position) = f.evalRhs(vs, es, fs, pos)
override def evalDefault(expr: Expr, vs: ValScope, es: EvalScope) = f.evalDefault(expr, vs, es)
override def evalDefault(expr: Expr, vs: ValScope, es: EvalScope) = {
evaluator.visitExpr(expr)(ValScope.empty)
}
}
case x => x
}
Expand Down
2 changes: 1 addition & 1 deletion sjsonnet/test/src-jvm-native/sjsonnet/FileTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ object FileTests extends TestSuite{
def eval(p: os.Path) = {
val interp = new Interpreter(
Map("var1" -> "\"test\"", "var2" -> """local f(a, b) = {[a]: b, "y": 2}; f("x", 1)"""),
Map("var1" -> "test", "var2" -> ujson.Obj("x" -> 1, "y" -> 2)),
Map("var1" -> "\"test\"", "var2" -> """{"x": 1, "y": 2}"""),
OsPath(testSuiteRoot),
importer = sjsonnet.SjsonnetMain.resolveImport(Array(OsPath(testSuiteRoot))),
parseCache = new DefaultParseCache
Expand Down
30 changes: 30 additions & 0 deletions sjsonnet/test/src/sjsonnet/Std0150FunctionsTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,36 @@ object Std0150FunctionsTests extends TestSuite {
check("""std.extVar("stdExtVarRecursive")""", 115)
}


test("tlaVars"){
val interpreter = new Interpreter(
Map(),
Map(
"num" -> "1",
"str" -> "\"hello\"",
"bool" -> "true",
"jsonArrNums" -> """[1, 2, 3]""",
"jsonObjBools" -> """{"hello": false}""",
"code" -> """local f(a, b) = {[a]: b, "y": 2}; f("x", 1)""",
"std" -> """std.length("hello")""",
),
DummyPath(),
Importer.empty,
parseCache = new DefaultParseCache,
)

def check(s: String, expected: ujson.Value) =
interpreter.interpret(s, DummyPath("(memory)")) ==> Right(expected)

check("""function(num) num""", 1)
check("""function(str) str""", "hello")
check("""function(bool) bool""", ujson.True)
check("""function(jsonArrNums) jsonArrNums""", ujson.Arr(1, 2, 3))
check("""function(jsonObjBools) jsonObjBools""", ujson.Obj("hello" -> false))
check("""function(code) code""", ujson.Obj("x" -> 1, "y" -> 2))
check("""function(std) std""", 5)
}

test("fold"){
eval("""std.foldr(function (acc, it) acc + " " + it, "jsonnet", "this is")""") ==>
ujson.Str("t e n n o s j this is")
Expand Down