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
85 changes: 75 additions & 10 deletions sjsonnet/src/sjsonnet/Evaluator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class Evaluator(resolver: CachedResolver,
case e: Val => e
case e: ApplyBuiltin1 => visitApplyBuiltin1(e)
case e: ApplyBuiltin2 => visitApplyBuiltin2(e)
case e: ApplyBuiltin3 => visitApplyBuiltin3(e)
case e: ApplyBuiltin4 => visitApplyBuiltin4(e)
case e: And => visitAnd(e)
case e: Or => visitOr(e)
case e: UnaryOp => visitUnaryOp(e)
Expand Down Expand Up @@ -266,21 +268,84 @@ class Evaluator(resolver: CachedResolver,
}
}

private def visitApplyBuiltin1(e: ApplyBuiltin1)(implicit scope: ValScope) =
e.func.evalRhs(visitExpr(e.a1), this, e.pos)
private def visitApplyBuiltin1(e: ApplyBuiltin1)(implicit scope: ValScope) = {
if (tailstrict) {
e.func.evalRhs(visitExpr(e.a1), this, e.pos)
} else if (e.tailstrict) {
tailstrict = true
val res = e.func.evalRhs(visitExpr(e.a1), this, e.pos)
tailstrict = false
res
} else {
e.func.evalRhs(visitAsLazy(e.a1), this, e.pos)
}
}

private def visitApplyBuiltin2(e: ApplyBuiltin2)(implicit scope: ValScope) = {
if (tailstrict) {
e.func.evalRhs(visitExpr(e.a1), visitExpr(e.a2), this, e.pos)
} else if (e.tailstrict) {
tailstrict = true
val res = e.func.evalRhs(visitExpr(e.a1), visitExpr(e.a2), this, e.pos)
tailstrict = false
res
} else {
e.func.evalRhs(visitAsLazy(e.a1), visitAsLazy(e.a2), this, e.pos)
}
}

private def visitApplyBuiltin3(e: ApplyBuiltin3)(implicit scope: ValScope) = {
if (tailstrict) {
e.func.evalRhs(visitExpr(e.a1), visitExpr(e.a2), visitExpr(e.a3), this, e.pos)
} else if (e.tailstrict) {
tailstrict = true
val res = e.func.evalRhs(visitExpr(e.a1), visitExpr(e.a2), visitExpr(e.a3), this, e.pos)
tailstrict = false
res
} else {
e.func.evalRhs(visitAsLazy(e.a1), visitAsLazy(e.a2), visitAsLazy(e.a3), this, e.pos)
}
}

private def visitApplyBuiltin2(e: ApplyBuiltin2)(implicit scope: ValScope) =
e.func.evalRhs(visitExpr(e.a1), visitExpr(e.a2), this, e.pos)
private def visitApplyBuiltin4(e: ApplyBuiltin4)(implicit scope: ValScope) = {
if (tailstrict) {
e.func.evalRhs(visitExpr(e.a1), visitExpr(e.a2), visitExpr(e.a3), visitExpr(e.a4), this, e.pos)
} else if (e.tailstrict) {
tailstrict = true
val res = e.func.evalRhs(visitExpr(e.a1), visitExpr(e.a2), visitExpr(e.a3), visitExpr(e.a4), this, e.pos)
tailstrict = false
res
} else {
e.func.evalRhs(visitAsLazy(e.a1), visitAsLazy(e.a2), visitAsLazy(e.a3), visitAsLazy(e.a4), this, e.pos)
}
}

private def visitApplyBuiltin(e: ApplyBuiltin)(implicit scope: ValScope) = {
val arr = new Array[Val](e.argExprs.length)
val arr = new Array[Lazy](e.argExprs.length)
var idx = 0
while (idx < e.argExprs.length) {
val boundIdx = idx
arr(idx) = visitExpr(e.argExprs(boundIdx))
idx += 1
if (tailstrict) {
while (idx < e.argExprs.length) {
arr(idx) = visitExpr(e.argExprs(idx))
idx += 1
}
e.func.evalRhs(arr, this, e.pos)
} else if (e.tailstrict) {
tailstrict = true
while (idx < e.argExprs.length) {
arr(idx) = visitExpr(e.argExprs(idx))
idx += 1
}
val res = e.func.evalRhs(arr, this, e.pos)
tailstrict = false
res
} else {
while (idx < e.argExprs.length) {
val boundIdx = idx
arr(idx) = visitAsLazy(e.argExprs(boundIdx))
idx += 1
}
e.func.evalRhs(arr, this, e.pos)
}
e.func.evalRhs(arr, this, e.pos)
}

def visitAssert(e: AssertExpr)(implicit scope: ValScope): Val = {
Expand Down
12 changes: 9 additions & 3 deletions sjsonnet/src/sjsonnet/Expr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,19 @@ object Expr{
case class Apply1(pos: Position, value: Expr, a1: Expr, tailstrict: Boolean) extends Expr
case class Apply2(pos: Position, value: Expr, a1: Expr, a2: Expr, tailstrict: Boolean) extends Expr
case class Apply3(pos: Position, value: Expr, a1: Expr, a2: Expr, a3: Expr, tailstrict: Boolean) extends Expr
case class ApplyBuiltin(pos: Position, func: Val.Builtin, argExprs: Array[Expr]) extends Expr {
case class ApplyBuiltin(pos: Position, func: Val.Builtin, argExprs: Array[Expr], tailstrict: Boolean) extends Expr {
override def exprErrorString: String = s"std.${func.functionName}"
}
case class ApplyBuiltin1(pos: Position, func: Val.Builtin1, a1: Expr) extends Expr {
case class ApplyBuiltin1(pos: Position, func: Val.Builtin1, a1: Expr, tailstrict: Boolean) extends Expr {
override def exprErrorString: String = s"std.${func.functionName}"
}
case class ApplyBuiltin2(pos: Position, func: Val.Builtin2, a1: Expr, a2: Expr) extends Expr {
case class ApplyBuiltin2(pos: Position, func: Val.Builtin2, a1: Expr, a2: Expr, tailstrict: Boolean) extends Expr {
override def exprErrorString: String = s"std.${func.functionName}"
}
case class ApplyBuiltin3(pos: Position, func: Val.Builtin3, a1: Expr, a2: Expr, a3: Expr, tailstrict: Boolean) extends Expr {
override def exprErrorString: String = s"std.${func.functionName}"
}
case class ApplyBuiltin4(pos: Position, func: Val.Builtin4, a1: Expr, a2: Expr, a3: Expr, a4: Expr, tailstrict: Boolean) extends Expr {
override def exprErrorString: String = s"std.${func.functionName}"
}
case class Select(pos: Position, value: Expr, name: String) extends Expr {
Expand Down
27 changes: 21 additions & 6 deletions sjsonnet/src/sjsonnet/ExprTransform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,36 @@ abstract class ExprTransform {
if((x2 eq x) && (y2 eq y) && (z2 eq z) && (a2 eq a)) expr
else Apply3(pos, x2, y2, z2, a2, tailstrict)

case ApplyBuiltin(pos, func, x) =>
case ApplyBuiltin(pos, func, x, tailstrict) =>
val x2 = transformArr(x)
if(x2 eq x) expr
else ApplyBuiltin(pos, func, x2)
else ApplyBuiltin(pos, func, x2, tailstrict)

case ApplyBuiltin1(pos, func, x) =>
case ApplyBuiltin1(pos, func, x, tailstrict) =>
val x2 = transform(x)
if(x2 eq x) expr
else ApplyBuiltin1(pos, func, x2)
else ApplyBuiltin1(pos, func, x2, tailstrict)

case ApplyBuiltin2(pos, func, x, y) =>
case ApplyBuiltin2(pos, func, x, y, tailstrict) =>
val x2 = transform(x)
val y2 = transform(y)
if((x2 eq x) && (y2 eq y)) expr
else ApplyBuiltin2(pos, func, x2, y2)
else ApplyBuiltin2(pos, func, x2, y2, tailstrict)

case ApplyBuiltin3(pos, func, x, y, z, tailstrict) =>
val x2 = transform(x)
val y2 = transform(y)
val z2 = transform(z)
if((x2 eq x) && (y2 eq y) && (z2 eq z)) expr
else ApplyBuiltin3(pos, func, x2, y2, z2, tailstrict)

case ApplyBuiltin4(pos, func, x, y, z, a, tailstrict) =>
val x2 = transform(x)
val y2 = transform(y)
val z2 = transform(z)
val a2 = transform(a)
if((x2 eq x) && (y2 eq y) && (z2 eq z) && (a2 eq a)) expr
else ApplyBuiltin4(pos, func, x2, y2, z2, a2, tailstrict)

case UnaryOp(pos, op, x) =>
val x2 = transform(x)
Expand Down
4 changes: 2 additions & 2 deletions sjsonnet/src/sjsonnet/Format.scala
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ object Format{

class PartialApplyFmt(fmt: String) extends Val.Builtin1("format", "values") {
val (leading, chunks) = fastparse.parse(fmt, format(_)).get.value
def evalRhs(values0: Val, ev: EvalScope, pos: Position): Val =
Val.Str(pos, format(leading, chunks, values0, pos)(ev))
def evalRhs(values0: Lazy, ev: EvalScope, pos: Position): Val =
Val.Str(pos, format(leading, chunks, values0.force, pos)(ev))
}
}
12 changes: 7 additions & 5 deletions sjsonnet/src/sjsonnet/StaticOptimizer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class StaticOptimizer(
case b2 @ BinaryOp(pos, lhs, BinaryOp.OP_in, ValidSuper(_, selfIdx)) =>
InSuper(pos, lhs, selfIdx)
case b2 @ BinaryOp(pos, lhs: Val.Str, BinaryOp.OP_%, rhs) =>
try ApplyBuiltin1(pos, new Format.PartialApplyFmt(lhs.value), rhs)
try ApplyBuiltin1(pos, new Format.PartialApplyFmt(lhs.value), rhs, tailstrict = false)
catch { case _: Exception => b2 }

case e @ Id(pos, name) =>
Expand Down Expand Up @@ -147,15 +147,17 @@ class StaticOptimizer(
case newArgs =>
tryStaticApply(pos, f, newArgs, tailstrict) match {
case null =>
val (f2, rargs) = f.specialize(newArgs) match {
val (f2, rargs) = f.specialize(newArgs, tailstrict) match {
case null => (f, newArgs)
case (f2, a2) => (f2, a2)
}
val alen = rargs.length
f2 match {
case f2: Val.Builtin1 if alen == 1 => Expr.ApplyBuiltin1(pos, f2, rargs(0))
case f2: Val.Builtin2 if alen == 2 => Expr.ApplyBuiltin2(pos, f2, rargs(0), rargs(1))
case _ if f2.params.names.length == alen => Expr.ApplyBuiltin(pos, f2, rargs)
case f2: Val.Builtin1 if alen == 1 => Expr.ApplyBuiltin1(pos, f2, rargs(0), tailstrict)
case f2: Val.Builtin2 if alen == 2 => Expr.ApplyBuiltin2(pos, f2, rargs(0), rargs(1), tailstrict)
case f2: Val.Builtin3 if alen == 3 => Expr.ApplyBuiltin3(pos, f2, rargs(0), rargs(1), rargs(2), tailstrict)
case f2: Val.Builtin4 if alen == 4 => Expr.ApplyBuiltin4(pos, f2, rargs(0), rargs(1), rargs(2), rargs(3), tailstrict)
case _ if f2.params.names.length == alen => Expr.ApplyBuiltin(pos, f2, rargs, tailstrict)
case _ => null
}
case e => e
Expand Down
Loading
Loading