All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sjsonnet.Materializer.scala Maven / Gradle / Ivy

The newest version!
package sjsonnet

import sjsonnet.Expr.{FieldName, Member, ObjBody}
import sjsonnet.Expr.Member.Visibility
import upickle.core.Visitor

/**
  * Serializes the given [[Val]] out to the given [[upickle.core.Visitor]],
  * which can transform it into [[ujson.Value]]s or directly serialize it
  * to `String`s
  */
abstract class Materializer {
  def storePos(pos: Position): Unit
  def storePos(v: Val): Unit

  def apply(v: Val)(implicit evaluator: EvalScope): ujson.Value = apply0(v, ujson.Value)
  def stringify(v: Val)(implicit evaluator: EvalScope): String = {
    apply0(v, new sjsonnet.Renderer()).toString
  }

  def apply0[T](v: Val, visitor: Visitor[T, T])
               (implicit evaluator: EvalScope): T = try {
    v match {
      case Val.Str(pos, s) => storePos(pos); visitor.visitString(s, -1)
      case obj: Val.Obj =>
        storePos(obj.pos)
        obj.triggerAllAsserts(obj)
        val objVisitor = visitor.visitObject(obj.visibleKeyNames.length , -1)
        val sort = !evaluator.settings.preserveOrder
        var prevKey: String = null
        obj.foreachElement(sort, evaluator.emptyMaterializeFileScopePos) { (k, v) =>
          storePos(v)
          objVisitor.visitKeyValue(objVisitor.visitKey(-1).visitString(k, -1))
          objVisitor.visitValue(
            apply0(v, objVisitor.subVisitor.asInstanceOf[Visitor[T, T]]),
            -1
          )
          if(sort) {
            if(prevKey != null && k.compareTo(prevKey) <= 0)
              Error.fail(s"""Internal error: Unexpected key "$k" after "$prevKey" in sorted object materialization""")
            prevKey = k
          }
        }
        objVisitor.visitEnd(-1)
      case Val.Num(pos, n) => storePos(pos); visitor.visitFloat64(n, -1)
      case xs: Val.Arr =>
        storePos(xs.pos);
        val arrVisitor = visitor.visitArray(xs.length, -1)
        var i = 0
        while(i < xs.length) {
          val sub = arrVisitor.subVisitor.asInstanceOf[Visitor[T, T]]
          arrVisitor.visitValue(apply0(xs.force(i), sub), -1)
          i += 1
        }
        arrVisitor.visitEnd(-1)
      case Val.True(pos) => storePos(pos); visitor.visitTrue(-1)
      case Val.False(pos) => storePos(pos); visitor.visitFalse(-1)
      case Val.Null(pos) => storePos(pos); visitor.visitNull(-1)
      case f: Val.Func =>
        apply0(
          f.apply(Materializer.emptyLazyArray, null, evaluator.emptyMaterializeFileScopePos),
          visitor
        )
    }

  }catch {case e: StackOverflowError =>
    Error.fail("Stackoverflow while materializing, possibly due to recursive value")
  }

  def reverse(pos: Position, v: ujson.Value): Val = v match{
    case ujson.True => Val.True(pos)
    case ujson.False => Val.False(pos)
    case ujson.Null => Val.Null(pos)
    case ujson.Num(n) => Val.Num(pos, n)
    case ujson.Str(s) => Val.Str(pos, s)
    case ujson.Arr(xs) => new Val.Arr(pos, xs.map(x => (() => reverse(pos, x)): Lazy).toArray[Lazy])
    case ujson.Obj(xs) =>
      val builder = new java.util.LinkedHashMap[String, Val.Obj.Member]
      for(x <- xs) {
        val v = new Val.Obj.Member(false, Visibility.Normal) {
          def invoke(self: Val.Obj, sup: Val.Obj, fs: FileScope, ev: EvalScope): Val = reverse(pos, x._2)
        }
        builder.put(x._1, v)
      }
      new Val.Obj(pos, builder, false, null, null)
  }

  def toExpr(v: ujson.Value)(implicit ev: EvalScope): Expr = v match{
    case ujson.True => Val.True(ev.emptyMaterializeFileScopePos)
    case ujson.False => Val.False(ev.emptyMaterializeFileScopePos)
    case ujson.Null => Val.Null(ev.emptyMaterializeFileScopePos)
    case ujson.Num(n) => Val.Num(ev.emptyMaterializeFileScopePos, n)
    case ujson.Str(s) => Val.Str(ev.emptyMaterializeFileScopePos, s)
    case ujson.Arr(xs) => Expr.Arr(ev.emptyMaterializeFileScopePos, xs.map(toExpr).toArray[Expr])
    case ujson.Obj(kvs) =>
      ObjBody.MemberList(
        ev.emptyMaterializeFileScopePos,
        null,
        for((k, v) <- kvs.toArray)
          yield Member.Field(ev.emptyMaterializeFileScopePos, FieldName.Fixed(k), false, null, Visibility.Normal, toExpr(v)),
        null
      )
  }

}

object Materializer extends Materializer {
  def storePos(pos: Position): Unit = ()
  def storePos(v: Val): Unit = ()

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy