<commit_before>package eu.inn.binders.value

import java.util.Date

import scala.language.dynamics
import scala.language.experimental.macros

sealed trait Value extends Any with Dynamic {
  def ~~[T](visitor: ValueVisitor[T]): T

  def asString: String = this ~~ Visitors.asStringVisitor

  def asBoolean: Boolean = this ~~ Visitors.asBooleanVisitor

  def asBigDecimal: BigDecimal = this ~~ Visitors.asBigDecimalVisitor

  def asInt: Int = asBigDecimal.toIntExact
  def asLong: Long = asBigDecimal.toLongExact
  def asDouble: Double = asBigDecimal.toDouble
  def asFloat: Float = asBigDecimal.toFloat
  def asDate: Date = new Date(asLong)

  def asMap: scala.collection.Map[String, Value] = this ~~ Visitors.asMapVisitor

  def asSeq: Seq[Value] = this ~~ Visitors.asSeqVisitor

  def isDefined: Boolean = !isNull

  def isNull: Boolean = this ~~ Visitors.isNullVisitor

  def isEmpty: Boolean = this ~~ Visitors.isEmptyVisitor

  def +(other: Value): Value = other

  def selectDynamic(name: String): Value = {
    asMap.getOrElse(
      if (name.startsWith("_") && name.length > 1) {
        name.substring(1)
      } else {
        name
      },
      Null
    )
  }
}

trait ValueVisitor[T] {
  def visitNumber(d: Number): T
  def visitText(d: Text): T
  def visitObj(d: Obj): T
  def visitLst(d: Lst): T
  def visitBool(d: Bool): T
  def visitNull(): T
}

case object Null extends Value {
  override def ~~[T](visitor: ValueVisitor[T]): T = visitor.visitNull()
}

case class Number(v: BigDecimal) extends AnyVal with Value {
  override def ~~[T](visitor: ValueVisitor[T]): T = visitor.visitNumber(this)
}

case class Text(v: String) extends AnyVal with Value {
  override def ~~[T](visitor: ValueVisitor[T]): T = visitor.visitText(this)
}

case class Obj(v: scala.collection.Map[String, Value]) extends AnyVal with Value{
  override def ~~[T](visitor: ValueVisitor[T]): T = visitor.visitObj(this)

  override def +(other: Value): Value = {
    other match {
      case o: Obj ⇒
        Obj(v ++ o.v.map {
          case (k, otherV) => k -> v.get(k).map { originalV ⇒
            originalV.+(otherV)
          }.getOrElse {
            otherV
          }
        })
      case _ ⇒
        super.+(other)
    }
  }
}

object Obj {
  def apply(): Obj = new Obj(Map.empty)
}

object ObjV {
  // currently there is only mutable effective map that preserves order
  // so we use LinkedHashMap for that
  def apply(v: (String,Value)*): Obj = new Obj(scala.collection.mutable.LinkedHashMap(v: _*))
}

case class Lst(v: Seq[Value]) extends AnyVal with Value{
  override def ~~[T](visitor: ValueVisitor[T]): T = visitor.visitLst(this)
}

object Lst {
  def apply(): Lst = Lst(Seq.empty)
}

object LstV { // can't be Lst :-(
  def apply(seq: Value*): Lst = Lst(seq)
}

trait Bool extends Value with Product {
  val v: Boolean
  override def ~~[T](visitor: ValueVisitor[T]): T = visitor.visitBool(this)
  def copy(o: Boolean): Bool
  override def equals(obj: scala.Any): Boolean = obj match {
    case null ⇒ false
    case Bool(o) ⇒ v == o
    case b: Boolean ⇒ v == b
    case _ ⇒ false
  }
  override def hashCode(): Int = if(v) 1 else 0
  override def toString: String = s"Bool($v)"
  override def productArity: Int = 1
  override def productElement(n: Int): Any = if (n == 0) v else throw new IndexOutOfBoundsException(s"$n is out of bounds")
}

object Bool {
  def apply(v: Boolean): Bool = if(v) True else False
  def unapply(b: Bool): Option[Boolean] = Some(b.v)
}

case object True extends Bool {
  val v: Boolean = true
  def copy(o: Boolean) = Bool(o)
}

case object False extends Bool {
  val v: Boolean = false<commit_msg>* don't allocate for empty Obj & Lst<commit_after>package eu.inn.binders.value

import java.util.Date

import scala.language.dynamics
import scala.language.experimental.macros

sealed trait Value extends Any with Dynamic {
  def ~~[T](visitor: ValueVisitor[T]): T

  def asString: String = this ~~ Visitors.asStringVisitor

  def asBoolean: Boolean = this ~~ Visitors.asBooleanVisitor

  def asBigDecimal: BigDecimal = this ~~ Visitors.asBigDecimalVisitor

  def asInt: Int = asBigDecimal.toIntExact
  def asLong: Long = asBigDecimal.toLongExact
  def asDouble: Double = asBigDecimal.toDouble
  def asFloat: Float = asBigDecimal.toFloat
  def asDate: Date = new Date(asLong)

  def asMap: scala.collection.Map[String, Value] = this ~~ Visitors.asMapVisitor

  def asSeq: Seq[Value] = this ~~ Visitors.asSeqVisitor

  def isDefined: Boolean = !isNull

  def isNull: Boolean = this ~~ Visitors.isNullVisitor

  def isEmpty: Boolean = this ~~ Visitors.isEmptyVisitor

  def +(other: Value): Value = other

  def selectDynamic(name: String): Value = {
    asMap.getOrElse(
      if (name.startsWith("_") && name.length > 1) {
        name.substring(1)
      } else {
        name
      },
      Null
    )
  }
}

trait ValueVisitor[T] {
  def visitNumber(d: Number): T
  def visitText(d: Text): T
  def visitObj(d: Obj): T
  def visitLst(d: Lst): T
  def visitBool(d: Bool): T
  def visitNull(): T
}

case object Null extends Value {
  override def ~~[T](visitor: ValueVisitor[T]): T = visitor.visitNull()
}

case class Number(v: BigDecimal) extends AnyVal with Value {
  override def ~~[T](visitor: ValueVisitor[T]): T = visitor.visitNumber(this)
}

case class Text(v: String) extends AnyVal with Value {
  override def ~~[T](visitor: ValueVisitor[T]): T = visitor.visitText(this)
}

case class Obj(v: scala.collection.Map[String, Value]) extends AnyVal with Value{
  override def ~~[T](visitor: ValueVisitor[T]): T = visitor.visitObj(this)

  override def +(other: Value): Value = {
    other match {
      case o: Obj ⇒
        Obj(v ++ o.v.map {
          case (k, otherV) => k -> v.get(k).map { originalV ⇒
            originalV.+(otherV)
          }.getOrElse {
            otherV
          }
        })
      case _ ⇒
        super.+(other)
    }
  }
}

object Obj {
  val empty = new Obj(Map.empty)
  def apply(): Obj = empty
}

object ObjV {
  // currently there is only mutable effective map that preserves order
  // so we use LinkedHashMap for that
  def apply(v: (String,Value)*): Obj = {
    if (v.isEmpty)
      Obj.empty
    else
      new Obj(scala.collection.mutable.LinkedHashMap(v: _*))
  }
}

case class Lst(v: Seq[Value]) extends AnyVal with Value{
  override def ~~[T](visitor: ValueVisitor[T]): T = visitor.visitLst(this)
}

object Lst {
  val empty = Lst(Seq.empty)
  def apply(): Lst = empty
}

object LstV { // can't be Lst :-(
  def apply(seq: Value*): Lst = {
    if (seq.isEmpty)
      Lst.empty
    else
      Lst(seq)
  }
}

trait Bool extends Value with Product {
  val v: Boolean
  override def ~~[T](visitor: ValueVisitor[T]): T = visitor.visitBool(this)
  def copy(o: Boolean): Bool
  override def equals(obj: scala.Any): Boolean = obj match {
    case null ⇒ false
    case Bool(o) ⇒ v == o
    case b: Boolean ⇒ v == b
    case _ ⇒ false
  }
  override def hashCode(): Int = if(v) 1 else 0
  override def toString: String = s"Bool($v)"
  override def productArity: Int = 1
  override def productElement(n: Int): Any = if (n == 0) v else throw new IndexOutOfBoundsException(s"$n is out of bounds")
}

object Bool {
  def apply(v: Boolean): Bool = if(v) True else False
  def unapply(b: Bool): Option[Boolean] = Some(b.v)
}

case object True extends Bool {
  val v: Boolean = true
  def copy(o: Boolean) = Bool(o)
}

case object False extends Bool {
  val v: Boolean = false
{'fraction_non_alphanumeric': 0.12387357094821788, 'fraction_numerical': 0.0016139878950907868, 'mean_word_length': 3.2735632183908048, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5004450', 'n_tokens_mistral': 2570, 'n_tokens_neox': 2535, 'n_words': 779}