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
11 changes: 10 additions & 1 deletion sjsonnet/src/sjsonnet/Materializer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ abstract class Materializer {
"Couldn't manifest function with params [" + s.params.names.mkString(",") + "]",
v.pos
)
case vv: Val =>
case mat: Materializer.Materializable => storePos(v.pos); mat.materialize(visitor)
case vv: Val =>
Error.fail("Unknown value type " + vv.prettyName, vv.pos)
case null =>
Error.fail("Unknown value type " + v)
Expand Down Expand Up @@ -147,4 +148,12 @@ object Materializer extends Materializer {

final val emptyStringArray = new Array[String](0)
final val emptyLazyArray = new Array[Lazy](0)

/**
* Trait for providing custom materialization logic to the Materializer.
* @since 1.0.0
*/
trait Materializable {
def materialize[T](visitor: Visitor[T, T])(implicit evaluator: EvalScope): T
}
}
69 changes: 69 additions & 0 deletions sjsonnet/test/src/sjsonnet/CustomValTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package sjsonnet

import upickle.core.Visitor
import utest._

object CustomValTests extends TestSuite {
private final case class ImportantString(pos: Position, str: String, importance: Int)
extends Val.Literal
with Materializer.Materializable {
override def prettyName: String = "Important string"
def materialize[T](visitor: Visitor[T, T])(implicit evaluator: EvalScope): T = {
visitor.visitString(str + "!".repeat(importance), -1)
}
}

private final class IncreaseImportance
extends Val.Builtin1("increaseImportance", "important_string") {
def evalRhs(arg1: Lazy, ev: EvalScope, pos: Position): Val = {
arg1.force match {
case importantString: ImportantString =>
importantString.copy(importance = importantString.importance + 1)
case other => other
}
}
}

private def variableResolve(name: String): Option[Expr] = {
if (name == "message") {
Some(ImportantString(new Position(null, 0), "message", 2))
} else if (name == "increaseImportance") {
Some(new IncreaseImportance().asFunc)
} else {
None
}
}

private val interpreter = new Interpreter(
Map(),
Map(),
DummyPath(),
Importer.empty,
parseCache = new DefaultParseCache,
variableResolver = variableResolve
)

def check(s: String)(f: Function[Any, Boolean]): Unit = {
val result = interpreter.interpret(s, DummyPath("(memory)"))
assertMatch(result) {
case Right(v) if f(v) => ()
case Left(e) => throw new Exception(s"check failed: $s, $e")
}
}

def tests: Tests = Tests {
test("test custom Val materialization") {
check(s"""important""") {
case ujson.Str(v) => v == "message!!"
case _ => false
}
}

test("test custom Val usage") {
check(s"""increaseImportance(message)""") {
case ujson.Str(v) => v == "message!!!"
case _ => false
}
}
}
}