Skip to content

Commit ad5ae74

Browse files
Fix tailstrict implementation and add more tests (#379)
Fixes #359
1 parent d7427fa commit ad5ae74

File tree

16 files changed

+250
-183
lines changed

16 files changed

+250
-183
lines changed

sjsonnet/src/sjsonnet/Evaluator.scala

Lines changed: 31 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package sjsonnet
22

33
import sjsonnet.Expr.Member.Visibility
4-
import sjsonnet.Expr.{Error => _, _}
4+
import sjsonnet.Expr.{Error as _, *}
55
import ujson.Value
66

77
import scala.annotation.{switch, tailrec}
@@ -29,7 +29,6 @@ class Evaluator(
2929
def materialize(v: Val): Value = Materializer.apply(v)
3030
val cachedImports: collection.mutable.HashMap[Path, Val] =
3131
collection.mutable.HashMap.empty[Path, Val]
32-
var tailstrict: Boolean = false
3332

3433
override def visitExpr(e: Expr)(implicit scope: ValScope): Val = try {
3534
e match {
@@ -202,49 +201,31 @@ class Evaluator(
202201
}
203202
}
204203

205-
protected def visitApply(e: Apply)(implicit scope: ValScope) = {
204+
protected def visitApply(e: Apply)(implicit scope: ValScope): Val = {
206205
val lhs = visitExpr(e.value)
206+
implicit val tailstrictMode: TailstrictMode =
207+
if (e.tailstrict) TailstrictModeEnabled else TailstrictModeDisabled
207208

208-
if (tailstrict) {
209+
if (e.tailstrict) {
209210
lhs.cast[Val.Func].apply(e.args.map(visitExpr(_)), e.namedNames, e.pos)
210-
} else if (e.tailstrict) {
211-
tailstrict = true
212-
val res = lhs.cast[Val.Func].apply(e.args.map(visitExpr(_)), e.namedNames, e.pos)
213-
tailstrict = false
214-
res
215211
} else {
216-
val args = e.args
217-
val argsL = new Array[Lazy](args.length)
218-
var idx = 0
219-
while (idx < args.length) {
220-
argsL(idx) = visitAsLazy(args(idx))
221-
idx += 1
222-
}
223-
lhs.cast[Val.Func].apply(argsL, e.namedNames, e.pos)
212+
lhs.cast[Val.Func].apply(e.args.map(visitAsLazy(_)), e.namedNames, e.pos)
224213
}
225214
}
226215

227216
protected def visitApply0(e: Apply0)(implicit scope: ValScope): Val = {
228217
val lhs = visitExpr(e.value)
229-
if (e.tailstrict) {
230-
tailstrict = true
231-
val res = lhs.cast[Val.Func].apply0(e.pos)
232-
tailstrict = false
233-
res
234-
} else {
235-
lhs.cast[Val.Func].apply0(e.pos)
236-
}
218+
implicit val tailstrictMode: TailstrictMode =
219+
if (e.tailstrict) TailstrictModeEnabled else TailstrictModeDisabled
220+
lhs.cast[Val.Func].apply0(e.pos)
237221
}
238222

239223
protected def visitApply1(e: Apply1)(implicit scope: ValScope): Val = {
240224
val lhs = visitExpr(e.value)
241-
if (tailstrict) {
225+
implicit val tailstrictMode: TailstrictMode =
226+
if (e.tailstrict) TailstrictModeEnabled else TailstrictModeDisabled
227+
if (e.tailstrict) {
242228
lhs.cast[Val.Func].apply1(visitExpr(e.a1), e.pos)
243-
} else if (e.tailstrict) {
244-
tailstrict = true
245-
val res = lhs.cast[Val.Func].apply1(visitExpr(e.a1), e.pos)
246-
tailstrict = false
247-
res
248229
} else {
249230
val l1 = visitAsLazy(e.a1)
250231
lhs.cast[Val.Func].apply1(l1, e.pos)
@@ -253,13 +234,11 @@ class Evaluator(
253234

254235
protected def visitApply2(e: Apply2)(implicit scope: ValScope): Val = {
255236
val lhs = visitExpr(e.value)
256-
if (tailstrict) {
237+
implicit val tailstrictMode: TailstrictMode =
238+
if (e.tailstrict) TailstrictModeEnabled else TailstrictModeDisabled
239+
240+
if (e.tailstrict) {
257241
lhs.cast[Val.Func].apply2(visitExpr(e.a1), visitExpr(e.a2), e.pos)
258-
} else if (e.tailstrict) {
259-
tailstrict = true
260-
val res = lhs.cast[Val.Func].apply2(visitExpr(e.a1), visitExpr(e.a2), e.pos)
261-
tailstrict = false
262-
res
263242
} else {
264243
val l1 = visitAsLazy(e.a1)
265244
val l2 = visitAsLazy(e.a2)
@@ -269,13 +248,11 @@ class Evaluator(
269248

270249
protected def visitApply3(e: Apply3)(implicit scope: ValScope): Val = {
271250
val lhs = visitExpr(e.value)
272-
if (tailstrict) {
251+
implicit val tailstrictMode: TailstrictMode =
252+
if (e.tailstrict) TailstrictModeEnabled else TailstrictModeDisabled
253+
254+
if (e.tailstrict) {
273255
lhs.cast[Val.Func].apply3(visitExpr(e.a1), visitExpr(e.a2), visitExpr(e.a3), e.pos)
274-
} else if (e.tailstrict) {
275-
tailstrict = true
276-
val res = lhs.cast[Val.Func].apply3(visitExpr(e.a1), visitExpr(e.a2), visitExpr(e.a3), e.pos)
277-
tailstrict = false
278-
res
279256
} else {
280257
val l1 = visitAsLazy(e.a1)
281258
val l2 = visitAsLazy(e.a2)
@@ -284,60 +261,34 @@ class Evaluator(
284261
}
285262
}
286263

287-
protected def visitApplyBuiltin0(e: ApplyBuiltin0) = {
288-
if (tailstrict) {
289-
e.func.evalRhs(this, e.pos)
290-
} else if (e.tailstrict) {
291-
tailstrict = true
292-
val res = e.func.evalRhs(this, e.pos)
293-
tailstrict = false
294-
res
295-
} else {
296-
e.func.evalRhs(this, e.pos)
297-
}
298-
}
264+
protected def visitApplyBuiltin0(e: ApplyBuiltin0): Val = e.func.evalRhs(this, e.pos)
299265

300-
protected def visitApplyBuiltin1(e: ApplyBuiltin1)(implicit scope: ValScope) = {
301-
if (tailstrict) {
266+
protected def visitApplyBuiltin1(e: ApplyBuiltin1)(implicit scope: ValScope): Val = {
267+
if (e.tailstrict) {
302268
e.func.evalRhs(visitExpr(e.a1), this, e.pos)
303-
} else if (e.tailstrict) {
304-
tailstrict = true
305-
val res = e.func.evalRhs(visitExpr(e.a1), this, e.pos)
306-
tailstrict = false
307-
res
308269
} else {
309270
e.func.evalRhs(visitAsLazy(e.a1), this, e.pos)
310271
}
311272
}
312273

313-
protected def visitApplyBuiltin2(e: ApplyBuiltin2)(implicit scope: ValScope) = {
314-
if (tailstrict) {
274+
protected def visitApplyBuiltin2(e: ApplyBuiltin2)(implicit scope: ValScope): Val = {
275+
if (e.tailstrict) {
315276
e.func.evalRhs(visitExpr(e.a1), visitExpr(e.a2), this, e.pos)
316-
} else if (e.tailstrict) {
317-
tailstrict = true
318-
val res = e.func.evalRhs(visitExpr(e.a1), visitExpr(e.a2), this, e.pos)
319-
tailstrict = false
320-
res
321277
} else {
322278
e.func.evalRhs(visitAsLazy(e.a1), visitAsLazy(e.a2), this, e.pos)
323279
}
324280
}
325281

326-
protected def visitApplyBuiltin3(e: ApplyBuiltin3)(implicit scope: ValScope) = {
327-
if (tailstrict) {
282+
protected def visitApplyBuiltin3(e: ApplyBuiltin3)(implicit scope: ValScope): Val = {
283+
if (e.tailstrict) {
328284
e.func.evalRhs(visitExpr(e.a1), visitExpr(e.a2), visitExpr(e.a3), this, e.pos)
329-
} else if (e.tailstrict) {
330-
tailstrict = true
331-
val res = e.func.evalRhs(visitExpr(e.a1), visitExpr(e.a2), visitExpr(e.a3), this, e.pos)
332-
tailstrict = false
333-
res
334285
} else {
335286
e.func.evalRhs(visitAsLazy(e.a1), visitAsLazy(e.a2), visitAsLazy(e.a3), this, e.pos)
336287
}
337288
}
338289

339-
protected def visitApplyBuiltin4(e: ApplyBuiltin4)(implicit scope: ValScope) = {
340-
if (tailstrict) {
290+
protected def visitApplyBuiltin4(e: ApplyBuiltin4)(implicit scope: ValScope): Val = {
291+
if (e.tailstrict) {
341292
e.func.evalRhs(
342293
visitExpr(e.a1),
343294
visitExpr(e.a2),
@@ -346,18 +297,6 @@ class Evaluator(
346297
this,
347298
e.pos
348299
)
349-
} else if (e.tailstrict) {
350-
tailstrict = true
351-
val res = e.func.evalRhs(
352-
visitExpr(e.a1),
353-
visitExpr(e.a2),
354-
visitExpr(e.a3),
355-
visitExpr(e.a4),
356-
this,
357-
e.pos
358-
)
359-
tailstrict = false
360-
res
361300
} else {
362301
e.func.evalRhs(
363302
visitAsLazy(e.a1),
@@ -370,27 +309,17 @@ class Evaluator(
370309
}
371310
}
372311

373-
protected def visitApplyBuiltin(e: ApplyBuiltin)(implicit scope: ValScope) = {
312+
protected def visitApplyBuiltin(e: ApplyBuiltin)(implicit scope: ValScope): Val = {
374313
val arr = new Array[Lazy](e.argExprs.length)
375314
var idx = 0
376315

377-
if (tailstrict) {
316+
if (e.tailstrict) {
378317
while (idx < e.argExprs.length) {
379318
arr(idx) = visitExpr(e.argExprs(idx))
380319
idx += 1
381320
}
382321
e.func.evalRhs(arr, this, e.pos)
383-
} else if (e.tailstrict) {
384-
tailstrict = true
385-
while (idx < e.argExprs.length) {
386-
arr(idx) = visitExpr(e.argExprs(idx))
387-
idx += 1
388-
}
389-
val res = e.func.evalRhs(arr, this, e.pos)
390-
tailstrict = false
391-
res
392322
} else {
393-
394323
while (idx < e.argExprs.length) {
395324
val boundIdx = idx
396325
arr(idx) = visitAsLazy(e.argExprs(boundIdx))

sjsonnet/src/sjsonnet/Interpreter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ class Interpreter(
197197
if (tlaExpressions.exists(_ eq expr)) ValScope.empty else vs
198198
)
199199
}
200-
}.apply0(f.pos)(evaluator)
200+
}.apply0(f.pos)(evaluator, TailstrictModeDisabled)
201201
case x => x
202202
}
203203
} yield res

sjsonnet/src/sjsonnet/StaticOptimizer.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@ class StaticOptimizer(
153153
tailstrict: Boolean): Expr = {
154154
if (f.staticSafe && args.forall(_.isInstanceOf[Val])) {
155155
val vargs = args.map(_.asInstanceOf[Val])
156-
try f.apply(vargs, null, pos)(ev).asInstanceOf[Expr]
156+
val tailstrictMode = if (tailstrict) TailstrictModeEnabled else TailstrictModeDisabled
157+
try f.apply(vargs, null, pos)(ev, tailstrictMode).asInstanceOf[Expr]
157158
catch { case _: Exception => null }
158159
} else null
159160
}

0 commit comments

Comments
 (0)