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
14 changes: 13 additions & 1 deletion sjsonnet/src/sjsonnet/Error.scala
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ object Error {
var frameStart = 0
// Collapse innermost frame name into message when flagged, hiding it from "at" lines
if (frames.size > 0 && rawFrames(0).collapseIntoMessage) {
msg = "[" + frames.get(0)._2 + "] " + msg
val name = frames.get(0)._2
msg = "[" + name + "] " + stripDuplicateMessagePrefix(msg, name)
frameStart = 1
}

Expand Down Expand Up @@ -186,6 +187,17 @@ object Error {
name.startsWith("std.") || name == "default" ||
name == "object comprehension" || name == "array comprehension"

private def stripDuplicateMessagePrefix(msg: String, name: String): String = {
if (name == null || !msg.startsWith(name) || msg.length <= name.length) msg
else {
msg.charAt(name.length) match {
case ':' => msg.substring(name.length + 1).stripPrefix(" ")
case ' ' => msg.substring(name.length + 1)
case _ => msg
}
}
}

private def appendFrame(sb: StringBuilder, f: Frame, name: String): Unit = {
sb.append("\n at [").append(name).append("]")
if (f.pos != null) {
Expand Down
116 changes: 58 additions & 58 deletions sjsonnet/src/sjsonnet/Evaluator.scala

Large diffs are not rendered by default.

63 changes: 33 additions & 30 deletions sjsonnet/src/sjsonnet/Format.scala
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,7 @@ object Format {
case _ =>
if (valuesArr != null && i >= valuesArr.length) {
Error.fail(
"Too few values to format: %d, expected at least %d".format(
"too few values to format: %d, expected at least %d".format(
valuesArr.length,
i + 1
)
Expand All @@ -629,7 +629,8 @@ object Format {
val key = if (labels == null) null else labels(idx)
val raw =
if (key == null) {
if (valuesArr == null) Error.fail("Invalid format values")
if (valuesArr == null)
Error.fail("format values must be an array or object")
// Fast path: skip star checks when format has no * specifiers
if (!parsed.hasAnyStar) valuesArr.value(i)
else
Expand All @@ -639,10 +640,7 @@ object Format {
val width = valuesArr.value(i)
if (!width.isInstanceOf[Val.Num]) {
Error.fail(
"A * was specified at position %d. An integer is expected for a width"
.format(
idx
)
"* width at position %d requires an integer".format(idx)
)
}
i += 1
Expand All @@ -652,8 +650,7 @@ object Format {
val precision = valuesArr.value(i)
if (!precision.isInstanceOf[Val.Num]) {
Error.fail(
"A * was specified at position %d. An integer is expected for a precision"
.format(idx)
"* precision at position %d requires an integer".format(idx)
)
}
i += 1
Expand All @@ -663,18 +660,14 @@ object Format {
val width = valuesArr.value(i)
if (!width.isInstanceOf[Val.Num]) {
Error.fail(
"A * was specified at position %d. An integer is expected for a width"
.format(
idx
)
"* width at position %d requires an integer".format(idx)
)
}
i += 1
val precision = valuesArr.value(i)
if (!precision.isInstanceOf[Val.Num]) {
Error.fail(
"A * was specified at position %d. An integer is expected for a precision"
.format(idx)
"* precision at position %d requires an integer".format(idx)
)
}
i += 1
Expand All @@ -683,12 +676,12 @@ object Format {
}
} else {
if (formatted.widthStar)
Error.fail("Cannot use * width with object.", pos)
Error.fail("cannot use * width with named format", pos)
if (formatted.precisionStar)
Error.fail("Cannot use * precision with object.", pos)
Error.fail("cannot use * precision with named format", pos)
if (valuesArr != null) valuesArr.value(i)
else if (valuesObj != null) valuesObj.value(key, pos)
else Error.fail("Invalid format values")
else Error.fail("format values must be an array or object")
}
// Direct Val dispatch: skip Materializer for common types (Str, Num, Bool, Null).
// This avoids the overhead of materializing to ujson.Value and then matching on it,
Expand All @@ -697,10 +690,12 @@ object Format {
if (resultAsciiSafe && !specOutputAsciiSafe(rawVal, formatted.conversion))
resultAsciiSafe = false
val formattedValue = rawVal match {
case f: Val.Func => Error.fail("Cannot format function value", f)
case f: Val.Func => Error.fail("cannot format function value", f)
case vs: Val.Str =>
if (formatted.conversion != 's' && formatted.conversion != 'c')
Error.fail("Format required a number at %d, got string".format(i))
Error.fail(
"expected number at position %d, got string".format(i)
)
widenRaw(formatted, vs.str)
case vn: Val.Num =>
val s = vn.asDouble
Expand All @@ -717,15 +712,17 @@ object Format {
case 'c' =>
val codePoint = s.toInt
if (codePoint < 0)
Error.fail("Codepoints must be >= 0, got " + codePoint)
Error.fail("codepoints must be >= 0, got " + codePoint)
if (codePoint > 0x10ffff)
Error.fail("Invalid unicode codepoint, got " + codePoint)
Error.fail("invalid unicode codepoint, got " + codePoint)
val c = if (codePoint >= 0xd800 && codePoint <= 0xdfff) 0xfffd else codePoint
widenRaw(formatted, Character.toString(c))
case 's' =>
widenRaw(formatted, RenderUtils.renderDouble(s))
case _ =>
Error.fail("Format required a %s at %d, got number".format(rawVal.prettyName, i))
Error.fail(
"unsupported format conversion at position %d, got number".format(i)
)
}
case _: Val.True =>
val b = 1
Expand All @@ -740,10 +737,12 @@ object Format {
case 'g' => formatGeneric(formatted, b).toLowerCase
case 'G' => formatGeneric(formatted, b)
case 'c' =>
Error.fail("%c expected number / string, got: boolean")
Error.fail("%c expected number or string, got boolean")
case 's' => widenRaw(formatted, "true")
case _ =>
Error.fail("Format required a %s at %d, got boolean".format(rawVal.prettyName, i))
Error.fail(
"expected number or string at position %d, got boolean".format(i)
)
}
case _: Val.False =>
val b = 0
Expand All @@ -758,17 +757,21 @@ object Format {
case 'g' => formatGeneric(formatted, b).toLowerCase
case 'G' => formatGeneric(formatted, b)
case 'c' =>
Error.fail("%c expected number / string, got: boolean")
Error.fail("%c expected number or string, got boolean")
case 's' => widenRaw(formatted, "false")
case _ =>
Error.fail("Format required a %s at %d, got boolean".format(rawVal.prettyName, i))
Error.fail(
"expected number or string at position %d, got boolean".format(i)
)
}
case _: Val.Null =>
formatted.conversion match {
case 's' => widenRaw(formatted, "null")
case 'c' => Error.fail("%c expected number / string, got: null")
case 'c' => Error.fail("%c expected number or string, got null")
case _ =>
Error.fail("Format required number at %d, got null".format(i))
Error.fail(
"expected number or string at position %d, got null".format(i)
)
}
case _ =>
// Complex types (Arr, Obj): materialize via Renderer
Expand All @@ -792,7 +795,7 @@ object Format {

if (valuesArr != null && i < valuesArr.length) {
Error.fail(
"Too many values to format: %d, expected %d".format(valuesArr.length, i)
"too many values to format: %d, expected %d".format(valuesArr.length, i)
)
}
val resultStr = if (singleSpecNoStatic) singleFormatted else output.toString()
Expand Down Expand Up @@ -910,7 +913,7 @@ object Format {
case _: Val.True => "true"
case _: Val.False => "false"
case _: Val.Null => "null"
case f: Val.Func => Error.fail("Cannot format function value", f)
case f: Val.Func => Error.fail("cannot format function value", f)
case other =>
// Complex types: materialize via Renderer
val value = other match {
Expand Down
6 changes: 3 additions & 3 deletions sjsonnet/src/sjsonnet/Util.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ object Util {
case Some(d) =>
val s = asSliceInt(d, "step")
if (s < 0) {
Error.fail(s"got [$start:$end:$s] but negative steps are not supported", pos)(ev)
Error.fail(s"Got [$start:$end:$s] but negative steps are not supported", pos)(ev)
} else if (s == 0) {
Error.fail(s"got $s but step must be greater than 0", pos)(ev)
Error.fail(s"Got $s but step must be greater than 0", pos)(ev)
}
s
}
Expand Down Expand Up @@ -199,7 +199,7 @@ object Util {
case _: Val.Func | _: Val.Obj | _: Val.Bool =>
Error.fail("Values of type " + t1 + " are not comparable.")
case _: Val.Null =>
Error.fail("binary operator < does not operate on null.")
Error.fail("Binary operator < does not operate on null.")
case _ =>
val cmp = ev.compare(v1, v2)
if (cmp < 0) -1 else if (cmp > 0) 1 else 0
Expand Down
24 changes: 12 additions & 12 deletions sjsonnet/src/sjsonnet/Val.scala
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ object Val {
}
final case class Num(var pos: Position, private val num: Double) extends Literal {
if (num.isInfinite) {
Error.fail("overflow")
Error.fail("Overflow")
}

def prettyName = "number"
Expand All @@ -486,11 +486,11 @@ object Val {

def asPositiveInt: Int = {
if (!num.isWhole || !num.isValidInt) {
Error.fail("index value is not a valid integer")
Error.fail("Index value is not a valid integer")
}

if (num.toInt < 0) {
Error.fail("index value is not a positive integer, got: " + num.toInt)
Error.fail("Index value is not a positive integer, got: " + num.toInt)
}
num.toInt
}
Expand All @@ -499,18 +499,18 @@ object Val {

def asSafeLong: Long = {
if (num.isInfinite || num.isNaN) {
Error.fail("numeric value is not finite")
Error.fail("Numeric value is not finite")
}

if (num < DOUBLE_MIN_SAFE_INTEGER || num > DOUBLE_MAX_SAFE_INTEGER) {
Error.fail("numeric value outside safe integer range for bitwise operation")
Error.fail("Numeric value outside safe integer range for bitwise operation")
}
num.toLong
}

override def asDouble: Double = {
if (num.isNaN) {
Error.fail("not a number")
Error.fail("Not a number")
}
num
}
Expand Down Expand Up @@ -719,7 +719,7 @@ object Val {
val leftLen = this.length
val rhsLen = rhs.length
val totalLenLong = leftLen.toLong + rhsLen.toLong
if (totalLenLong > Int.MaxValue) Error.fail("array too large")
if (totalLenLong > Int.MaxValue) Error.fail("Array too large")
val totalLen = totalLenLong.toInt
// Use lazy concat when the result is large enough that avoiding the
// arraycopy is worthwhile. Limit to depth-1 (neither side is a concat view)
Expand Down Expand Up @@ -935,7 +935,7 @@ object Val {
// materialize later anyway; eagerly copying here multiplies thunks and stresses young-gen GC.
private val sourceLen = source.length
private val totalLen = sourceLen.toLong * count.toLong
if (totalLen > Int.MaxValue) throw new IllegalArgumentException("array too large")
if (totalLen > Int.MaxValue) throw new IllegalArgumentException("Array too large")
_length = totalLen.toInt

override def value(i: Int): Val =
Expand Down Expand Up @@ -2633,13 +2633,13 @@ object Val {
val idx = params.paramMap.getOrElse(
namedNames(i),
Error.fail(
s"has no parameter ${namedNames(i)}",
s"Has no parameter ${namedNames(i)}",
outerPos
)
)
if (argVals(base + idx) != null)
Error.fail(
s"binding parameter a second time: ${namedNames(i)}$functionNameSuffix",
s"Binding parameter a second time: ${namedNames(i)}$functionNameSuffix",
outerPos
)
argVals(base + idx) = argsL(j)
Expand Down Expand Up @@ -2672,7 +2672,7 @@ object Val {
if (missing != null) {
val plural = if (missing.size > 1) "s" else ""
Error.fail(
s"parameter$plural ${missing.mkString(", ")} not bound in call",
s"Parameter$plural ${missing.mkString(", ")} not bound in call",
outerPos
)
}
Expand Down Expand Up @@ -3055,7 +3055,7 @@ object TailCall {
case unknown =>
val op = if (booleanIsAnd) "&&" else "||"
Error.fail(
s"binary operator $op does not operate on ${unknown.prettyName}s.",
s"Binary operator $op does not operate on ${unknown.prettyName}s.",
booleanPos
)
}
Expand Down
Loading
Loading